Phase 2 of test cleanup: moving test files from AndroidTests closer to their sources.

Most of these are file moves; a couple notable exceptions are the changes due to the move, and fixing up test code:
- database/DatabaseCursorTest.java
- database/DatabaseStatementTest.java
- net/UriTest.java
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 77c176f..1fb9852 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -5,10 +5,14 @@
 LOCAL_MODULE_TAGS := tests
 
 # Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := \
+	$(call all-java-files-under, src) \
+	src/android/os/IAidlTest.aidl
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 
+LOCAL_CERTIFICATE := platform
+
 include $(BUILD_PACKAGE)
 
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 02961f4..8d7e187 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -16,6 +16,46 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.frameworks.coretests">
+
+    <permission android:name="com.android.frameworks.coretests.permission.TEST_GRANTED"
+        android:protectionLevel="normal"
+            android:label="@string/permlab_testGranted"
+            android:description="@string/permdesc_testGranted">
+        <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
+        <meta-data android:name="com.android.frameworks.coretests.boolean" android:value="true" />
+        <meta-data android:name="com.android.frameworks.coretests.integer" android:value="100" />
+        <meta-data android:name="com.android.frameworks.coretests.color" android:value="#ff000000" />
+        <meta-data android:name="com.android.frameworks.coretests.float" android:value="100.1" />
+        <meta-data android:name="com.android.frameworks.coretests.reference" android:resource="@xml/metadata" />
+    </permission>
+    <permission android:name="com.android.frameworks.coretests.permission.TEST_DENIED"
+        android:protectionLevel="normal"
+            android:label="@string/permlab_testDenied"
+            android:description="@string/permdesc_testDenied" />
+
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+    <uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
+    <uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
+    <uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
+    <uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <uses-permission android:name="android.permission.WRITE_SMS"/>
+    <uses-permission android:name="android.permission.TEST_GRANTED" />
+    <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
+    <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
+    <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH.ALL_SERVICES" />
+
     <uses-permission android:name="android.permission.RECEIVE_SMS"/>
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
@@ -28,12 +68,8 @@
     <!-- location test permissions -->
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>
-    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
-    
     <uses-permission android:name="android.permission.HARDWARE_TEST" />
-    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_VIEW_TYPES" />
     <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" />
     <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" />
@@ -975,6 +1011,187 @@
             </intent-filter>            
         </activity>
         
+
+
+        <!-- Activity-level metadata -->
+        <meta-data android:name="com.android.frameworks.coretests.isApp" android:value="true" />
+        <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
+        <meta-data android:name="com.android.frameworks.coretests.boolean" android:value="true" />
+        <meta-data android:name="com.android.frameworks.coretests.integer" android:value="100" />
+        <meta-data android:name="com.android.frameworks.coretests.color" android:value="#ff000000" />
+        <meta-data android:name="com.android.frameworks.coretests.float" android:value="100.1" />
+        <meta-data android:name="com.android.frameworks.coretests.reference"
+                   android:resource="@xml/metadata_app" />
+
+        <activity android:name="AndroidPerformanceTests" android:label="Android Performance Tests">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.UNIT_TEST" />
+            </intent-filter>
+        </activity>
+
+        <!-- Application components used for activity tests -->
+
+        <activity android:name="android.app.activity.TestedActivity"
+                android:process=":remoteActivity">
+        </activity>
+        <activity android:name="android.app.activity.LocalActivity" android:multiprocess="true">
+            <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
+            <meta-data android:name="com.android.frameworks.coretests.boolean" android:value="true" />
+            <meta-data android:name="com.android.frameworks.coretests.integer" android:value="100" />
+            <meta-data android:name="com.android.frameworks.coretests.color" android:value="#ff000000" />
+            <meta-data android:name="com.android.frameworks.coretests.float" android:value="100.1" />
+            <meta-data android:name="com.android.frameworks.coretests.reference" android:resource="@xml/metadata" />
+        </activity>
+        <activity android:name="android.app.activity.TestedScreen"
+                android:process=":remoteScreen">
+        </activity>
+        <activity android:name="android.app.activity.LocalScreen" android:multiprocess="true">
+        </activity>
+        <activity android:name="android.app.activity.ClearTop" android:multiprocess="true"
+                android:launchMode="singleTop">
+        </activity>
+        <activity android:name="android.app.activity.LocalDialog" android:multiprocess="true"
+                android:theme="@android:style/Theme.Dialog">
+        </activity>
+        <activity android:name="android.app.activity.SubActivityScreen">
+        </activity>
+        <activity android:name="android.app.activity.RemoteSubActivityScreen"
+                android:process=":remoteActivity">
+        </activity>
+        <activity android:name="android.app.activity.LaunchpadActivity" android:multiprocess="true">
+        </activity>
+        <activity android:name="android.app.activity.LaunchpadTabActivity" android:multiprocess="true">
+        </activity>
+
+        <receiver android:name="android.app.activity.AbortReceiver">
+            <intent-filter android:priority="1">
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_ABORT" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name="android.app.activity.LocalReceiver">
+            <intent-filter android:priority="-1">
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_ABORT" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_ALL" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_REPEAT" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_LOCAL" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_FAIL_REGISTER" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_FAIL_BIND" />
+            </intent-filter>
+            <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
+            <meta-data android:name="com.android.frameworks.coretests.boolean" android:value="true" />
+            <meta-data android:name="com.android.frameworks.coretests.integer" android:value="100" />
+            <meta-data android:name="com.android.frameworks.coretests.color" android:value="#ff000000" />
+            <meta-data android:name="com.android.frameworks.coretests.float" android:value="100.1" />
+            <meta-data android:name="com.android.frameworks.coretests.reference" android:resource="@xml/metadata" />
+        </receiver>
+        <receiver android:name="android.app.activity.ResultReceiver">
+            <intent-filter>
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_RESULT" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name="android.app.activity.LocalGrantedReceiver"
+                android:permission="com.android.frameworks.coretests.permission.TEST_GRANTED">
+            <intent-filter android:priority="-1">
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_LOCAL_GRANTED" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name="android.app.activity.LocalDeniedReceiver"
+                android:permission="com.android.frameworks.coretests.permission.TEST_DENIED">
+            <intent-filter android:priority="-1">
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_LOCAL_DENIED" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name="android.app.activity.RemoteReceiver"
+                android:process=":remoteReceiver">
+            <intent-filter android:priority="2">
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_ABORT" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_ALL" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_REPEAT" />
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_REMOTE" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name="android.app.activity.RemoteGrantedReceiver"
+                android:permission="com.android.frameworks.coretests.permission.TEST_GRANTED">
+            <intent-filter android:priority="2">
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_REMOTE_GRANTED" />
+            </intent-filter>
+        </receiver>
+        <receiver android:name="android.app.activity.RemoteDeniedReceiver"
+                android:permission="com.android.frameworks.coretests.permission.TEST_DENIED">
+            <intent-filter android:priority="2">
+                <action android:name="com.android.frameworks.coretests.activity.BROADCAST_REMOTE_DENIED" />
+            </intent-filter>
+        </receiver>
+        <service android:name="android.app.activity.LocalService">
+            <intent-filter>
+                <action android:name="com.android.frameworks.coretests.activity.SERVICE_LOCAL" />
+            </intent-filter>
+            <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
+            <meta-data android:name="com.android.frameworks.coretests.boolean" android:value="true" />
+            <meta-data android:name="com.android.frameworks.coretests.integer" android:value="100" />
+            <meta-data android:name="com.android.frameworks.coretests.color" android:value="#ff000000" />
+            <meta-data android:name="com.android.frameworks.coretests.float" android:value="100.1" />
+            <meta-data android:name="com.android.frameworks.coretests.reference" android:resource="@xml/metadata" />
+        </service>
+        <service android:name="android.app.activity.LocalDeniedService"
+                android:permission="com.android.frameworks.coretests.permission.TEST_DENIED">
+            <intent-filter>
+                <action android:name="com.android.frameworks.coretests.activity.SERVICE_LOCAL_DENIED" />
+            </intent-filter>
+        </service>
+        <service android:name="android.app.activity.LocalGrantedService"
+                android:permission="com.android.frameworks.coretests.permission.TEST_GRANTED">
+            <intent-filter>
+                <action android:name="com.android.frameworks.coretests.activity.SERVICE_LOCAL_GRANTED" />
+            </intent-filter>
+        </service>
+
+        <provider android:name="android.app.activity.LocalProvider"
+                android:authorities="com.android.frameworks.coretests.LocalProvider">
+            <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
+            <meta-data android:name="com.android.frameworks.coretests.boolean" android:value="true" />
+            <meta-data android:name="com.android.frameworks.coretests.integer" android:value="100" />
+            <meta-data android:name="com.android.frameworks.coretests.color" android:value="#ff000000" />
+            <meta-data android:name="com.android.frameworks.coretests.float" android:value="100.1" />
+            <meta-data android:name="com.android.frameworks.coretests.reference" android:resource="@xml/metadata" />
+        </provider>
+
+        <!-- Application components used for content tests -->
+        <provider android:name="android.content.MemoryFileProvider"
+                android:authorities="android.content.MemoryFileProvider"
+                android:process=":MemoryFileProvider">
+        </provider>
+
+        <!-- Application components used for os tests -->
+
+        <service android:name="android.os.MessengerService"
+                android:process=":messengerService">
+        </service>
+
+        <!-- Application components used for search manager tests -->
+
+        <activity android:name="android.app.activity.SearchableActivity"
+                android:label="Searchable Activity">
+            <intent-filter>
+                <action android:name="android.intent.action.SEARCH" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="android.app.searchable"
+                    android:resource="@xml/searchable" />
+        </activity>
+
+        <provider android:name="android.app.SuggestionProvider"
+                android:authorities="android.app.SuggestionProvider">
+        </provider>
+
+        <!-- Used to test IPC. -->
+        <service android:name="com.android.frameworks.coretests.binder.BinderTestService"
+                 android:process="binder.BinderTestService" />
+        <service android:name="com.android.frameworks.coretests.binder.BinderPerformanceService"
+                 android:process="binder.BinderPerformanceService" />
+        <service android:name="com.android.frameworks.coretests.binder.BinderVsMessagingService"
+                 android:process="binder.BinderVsMessagingService" />
     </application>
 
     <instrumentation
diff --git a/core/tests/coretests/assets/text.txt b/core/tests/coretests/assets/text.txt
new file mode 100644
index 0000000..3d8c519
--- /dev/null
+++ b/core/tests/coretests/assets/text.txt
@@ -0,0 +1 @@
+OneTwoThreeFourFiveSixSevenEightNineTen
\ No newline at end of file
diff --git a/core/tests/coretests/res/layout/layout_five.xml b/core/tests/coretests/res/layout/layout_five.xml
new file mode 100644
index 0000000..9923eaf
--- /dev/null
+++ b/core/tests/coretests/res/layout/layout_five.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT 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:id="@+id/content" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+	<TextView android:id="@+id/text" android:text="@string/layout_five_text_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/core/tests/coretests/res/layout/layout_four.xml b/core/tests/coretests/res/layout/layout_four.xml
new file mode 100644
index 0000000..e5a78bc
--- /dev/null
+++ b/core/tests/coretests/res/layout/layout_four.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT 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:id="@+id/text" android:text="@string/layout_four_text_text"/>
diff --git a/core/tests/coretests/res/layout/layout_one.xml b/core/tests/coretests/res/layout/layout_one.xml
new file mode 100644
index 0000000..6966246
--- /dev/null
+++ b/core/tests/coretests/res/layout/layout_one.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+<view xmlns:android="http://schemas.android.com/apk/res/android" class="android.view.InflateTest$ViewOne" android:id="@+id/viewOne" android:layout_width="match_parent" android:layout_height="match_parent"/>
diff --git a/core/tests/coretests/res/layout/layout_six.xml b/core/tests/coretests/res/layout/layout_six.xml
new file mode 100644
index 0000000..b78082d
--- /dev/null
+++ b/core/tests/coretests/res/layout/layout_six.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT 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:id="@+id/content" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+	<TextView android:id="@+id/text" android:text="@string/layout_six_text_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView android:id="@+id/text" android:text="@string/layout_six_text_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView android:id="@+id/text" android:text="@string/layout_six_text_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView android:id="@+id/text" android:text="@string/layout_six_text_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView android:id="@+id/text" android:text="@string/layout_six_text_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView android:id="@+id/text" android:text="@string/layout_six_text_text" android:layout_width="match_parent" android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/core/tests/coretests/res/layout/layout_tag.xml b/core/tests/coretests/res/layout/layout_tag.xml
new file mode 100644
index 0000000..7fb0489
--- /dev/null
+++ b/core/tests/coretests/res/layout/layout_tag.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+    class="android.view.InflateTest$ViewOne"
+    android:id="@+id/viewOne" android:tag="MyTag"
+    android:layout_width="match_parent" android:layout_height="match_parent"/>
diff --git a/core/tests/coretests/res/layout/layout_three.xml b/core/tests/coretests/res/layout/layout_three.xml
new file mode 100644
index 0000000..7242fc8
--- /dev/null
+++ b/core/tests/coretests/res/layout/layout_three.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT 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:id="@+id/content" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+	<view class="android.view.InflateTest$ViewOne" android:id="@+id/view1" android:layout_width="match_parent" android:layout_height="match_parent"/>
+	<view class="android.view.InflateTest$ViewOne" android:id="@+id/view2" android:layout_width="match_parent" android:layout_height="match_parent"/>
+	<view class="android.view.InflateTest$ViewOne" android:id="@+id/view3" android:layout_width="match_parent" android:layout_height="match_parent"/>
+	<view class="android.view.InflateTest$ViewOne" android:id="@+id/view4" android:layout_width="match_parent" android:layout_height="match_parent"/>
+	<view class="android.view.InflateTest$ViewOne" android:id="@+id/view5" android:layout_width="match_parent" android:layout_height="match_parent"/>
+	<view class="android.view.InflateTest$ViewOne" android:id="@+id/view6" android:layout_width="match_parent" android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/core/tests/coretests/res/layout/layout_two.xml b/core/tests/coretests/res/layout/layout_two.xml
new file mode 100644
index 0000000..9fb7d3b
--- /dev/null
+++ b/core/tests/coretests/res/layout/layout_two.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT 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:id="@+id/content" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+	<view class="android.view.InflateTest$ViewOne" android:id="@+id/viewOne" android:layout_width="match_parent" android:layout_height="match_parent"/>
+</LinearLayout>
+
diff --git a/core/tests/coretests/res/raw/medium.xml b/core/tests/coretests/res/raw/medium.xml
new file mode 100644
index 0000000..5757a24
--- /dev/null
+++ b/core/tests/coretests/res/raw/medium.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT 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 id="@+id/content" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
+	<TextView id="@+id/text" android:text="S" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView id="@+id/text" android:text="M" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView id="@+id/text" android:text="T" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView id="@+id/text" android:text="W" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView id="@+id/text" android:text="H" android:layout_width="match_parent" android:layout_height="wrap_content" />
+	<TextView id="@+id/text" android:text="F" android:layout_width="match_parent" android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/core/tests/coretests/res/raw/small.xml b/core/tests/coretests/res/raw/small.xml
new file mode 100644
index 0000000..ee859b9
--- /dev/null
+++ b/core/tests/coretests/res/raw/small.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+<view class="com.android.tests.InflateTest$ViewOne" id="@+id/viewOne" android:layout_width="match_parent" android:layout_height="match_parent"/>
diff --git a/core/tests/coretests/res/raw/text.txt b/core/tests/coretests/res/raw/text.txt
new file mode 100644
index 0000000..3d8c519
--- /dev/null
+++ b/core/tests/coretests/res/raw/text.txt
@@ -0,0 +1 @@
+OneTwoThreeFourFiveSixSevenEightNineTen
\ No newline at end of file
diff --git a/core/tests/coretests/res/raw/v21_backslash.vcf b/core/tests/coretests/res/raw/v21_backslash.vcf
new file mode 100644
index 0000000..bd3002b
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_backslash.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD

+VERSION:2.1

+N:;A\;B\\;C\\\;;D;\:E;\\\\;

+FN:A;B\C\;D:E\\

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_complicated.vcf b/core/tests/coretests/res/raw/v21_complicated.vcf
new file mode 100644
index 0000000..de34e16
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_complicated.vcf
@@ -0,0 +1,106 @@
+BEGIN:VCARD

+VERSION:2.1

+N:Gump;Forrest;Hoge;Pos;Tao

+FN:Joe Due

+ORG:Gump Shrimp Co.;Sales Dept.\;Manager;Fish keeper

+ROLE:Fish Cake Keeper!

+X-CLASS:PUBLIC

+TITLE:Shrimp Man

+TEL;WORK;VOICE:(111) 555-1212

+TEL;HOME;VOICE:(404) 555-1212

+TEL;CELL:0311111111

+TEL;VIDEO:0322222222

+TEL;VOICE:0333333333

+ADR;WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America

+LABEL;WORK;ENCODING=QUOTED-PRINTABLE:100 Waters Edge=0D=0ABaytown, LA 30314=0D=0AUnited  States of America

+ADR;HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America

+LABEL;HOME;ENCODING=QUOTED-PRINTABLE:42 Plantation St.=0D=0A=

+Baytown, LA 30314=0D=0A=

+United  States of America

+EMAIL;PREF;INTERNET:forrestgump@walladalla.com

+EMAIL;CELL:cell@example.com

+NOTE:The following note is the example from RFC 2045.

+NOTE;ENCODING=QUOTED-PRINTABLE:Now's the time =

+for all folk to come=

+ to the aid of their country.

+

+PHOTO;ENCODING=BASE64;TYPE=JPEG:

+ /9j/4QoPRXhpZgAATU0AKgAAAAgADQEOAAIAAAAPAAAAqgEPAAIAAAAHAAAAugEQAAIAAAAG

+ AAAAwgESAAMAAAABAAEAAAEaAAUAAAABAAAAyAEbAAUAAAABAAAA0AEoAAMAAAABAAIAAAEx

+ AAIAAAAOAAAA2AEyAAIAAAAUAAAA5gITAAMAAAABAAEAAIKYAAIAAAAOAAAA+odpAAQAAAAB

+ AAABhMSlAAcAAAB8AAABCAAABB4yMDA4MTAyOTEzNTUzMQAARG9Db01vAABEOTA1aQAAAABI

+ AAAAAQAAAEgAAAABRDkwNWkgVmVyMS4wMAAyMDA4OjEwOjI5IDEzOjU1OjQ3ACAgICAgICAg

+ ICAgICAAUHJpbnRJTQAwMzAwAAAABgABABQAFAACAQAAAAADAAAANAEABQAAAAEBAQAAAAEQ

+ gAAAAAAAEQkAACcQAAAPCwAAJxAAAAWXAAAnEAAACLAAACcQAAAcAQAAJxAAAAJeAAAnEAAA

+ AIsAACcQAAADywAAJxAAABvlAAAnEAAogpoABQAAAAEAAANqgp0ABQAAAAEAAANyiCIAAwAA

+ AAEAAgAAkAAABwAAAAQwMjIwkAMAAgAAABQAAAN6kAQAAgAAABQAAAOOkQEABwAAAAQBAgMA

+ kQIABQAAAAEAAAOikgEACgAAAAEAAAOqkgIABQAAAAEAAAOykgQACgAAAAEAAAO6kgUABQAA

+ AAEAAAPCkgcAAwAAAAEAAgAAkggAAwAAAAEAAAAAkgkAAwAAAAEAAAAAkgoABQAAAAEAAAPK

+ knwABwAAAAEAAAAAkoYABwAAABYAAAPSoAAABwAAAAQwMTAwoAEAAwAAAAEAAQAAoAIAAwAA

+ AAEAYAAAoAMAAwAAAAEASAAAoAUABAAAAAEAAAQAog4ABQAAAAEAAAPoog8ABQAAAAEAAAPw

+ ohAAAwAAAAEAAgAAohcAAwAAAAEAAgAAowAABwAAAAEDAAAAowEABwAAAAEBAAAApAEAAwAA

+ AAEAAAAApAIAAwAAAAEAAAAApAMAAwAAAAEAAAAApAQABQAAAAEAAAP4pAUAAwAAAAEAHQAA

+ pAYAAwAAAAEAAAAApAcAAwAAAAEAAAAApAgAAwAAAAEAAAAApAkAAwAAAAEAAAAApAoAAwAA

+ AAEAAAAApAwAAwAAAAEAAgAAAAAAAAAAAFMAACcQAAABXgAAAGQyMDA4OjEwOjI5IDEzOjU1

+ OjMxADIwMDg6MTA6MjkgMTM6NTU6NDcAAAApiAAAGwAAAAKyAAAAZAAAAV4AAABkAAAAAAAA

+ AGQAAAAlAAAACgAADpIAAAPoAAAAAAAAAAAyMDA4MTAyOTEzNTUzMQAAICoAAAAKAAAq4gAA

+ AAoAAAAAAAAAAQACAAEAAgAAAARSOTgAAAIABwAAAAQwMTAwAAAAAAAGAQMAAwAAAAEABgAA

+ ARoABQAAAAEAAARsARsABQAAAAEAAAR0ASgAAwAAAAEAAgAAAgEABAAAAAEAAAR8AgIABAAA

+ AAEAAAWLAAAAAAAAAEgAAAABAAAASAAAAAH/2P/bAIQAIBYYHBgUIBwaHCQiICYwUDQwLCww

+ YkZKOlB0Znp4cmZwboCQuJyAiK6KbnCg2qKuvsTO0M58muLy4MjwuMrOxgEiJCQwKjBeNDRe

+ xoRwhMbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbG

+ /8AAEQgAeACgAwEhAAIRAQMRAf/EAaIAAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKCxAA

+ AgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK

+ FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWG

+ h4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl

+ 5ufo6erx8vP09fb3+Pn6AQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgsRAAIBAgQEAwQH

+ BQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBka

+ JicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKT

+ lJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz

+ 9PX29/j5+v/aAAwDAQACEQMRAD8AFFSqKkZIoqRVpgSKKeBTEOApwFADsUYpgIRSEUANIppF

+ ICNhUTCgCMio2FICJhULCgC0oqVaAJFFSqKBkgFOApiHCnCgB2KMUCENJQA0imEUDGMKiYUA

+ RtUbUgIWqJhQBZSpFoAlWpVoGPFPFMQ7tSK2ODQA4yKO9HmKe9FxAzDHFIOlAAaYaAGNUTUD

+ ImqNqQETVE1AE6VKKAJFNSqaAHg08GmANIFFQM5Y5qJMBuT60ZNQIcrkVYSQMKuLGKaaasQx

+ qiagZE1RtSAjaomoAkQ1KpoAlU1IpoAkU07OBTArO+5qkV12Y71lfUBmaKkCRSuznrTFba2a

+ oCwGyM0E1qIjY1GxoGRNUZNICNqiagByGplNAEimpFNMB4YDvSucpxSYEIU04KazsAu1qArU

+ WELtPpTSposBNETt5pxNaoCNjUbGgCNjUZoGRtUTUgFU1KpoAkBqQHigCFnO7rUqOdlZp6gA

+ c+tODn1pXAXzD60eYfWncQvmNSGQ07gOMhCVEJGz1ptgS5yKYxqwGE1GxoAiamGkMapqVTQB

+ Kpp+eKAICfmqWM/Kaz6gANOBqQFzRmmAuaTNACsfkqMHmm9wJs8U0mtRDGNRsaAI2phpDI1N

+ SqaAJFNSA8UCISfmqSM/Kaz6jAHmnA1ICg0uaAFzSZpgKx+SmDrTe4E2eKaTWoiMmmMaAIzT

+ DSGRKakU0ASKaeDTERseakjPyms+oxAacDUgOBpc0gFzSZpgOY/KKYv3qrqIlpprQBjGoyaA

+ GGmmkMgU1IppgPBqQGgQu0Gn4wvFKwEQpwNZDHZpc0ALmigRKBleaQKBWtgA001QDGqM0gGm

+ mGkMrqakBoAepp4NMRIDTwaAE2A008GokgHxjd1pzKFpW0uAg5NSBBTirgOpDWgDTTTQAw0w

+ 0gGGmmgZWBp4pASKaeDTEOBp4NADwajbrUyBEkXWnSUdAGr1qeiAMSkNWAhphoAaaYaQDDTT

+ SGVRTwaYDxTwaBDwaeDQA4GlK5oauIeo20pGaLaAKqgU6hKwBSGmAhphoAaaYaQxhpppDKgN

+ PFMB4p4oEPFOBpgPBp4NAhwpwoAWloAKSgBDTTQMYaYaQDTTTSGA/9n/2wCEAAoHBwgHBgoI

+ CAgLCgoLDhgQDg0NDh0VFhEYIx8lJCIfIiEmKzcvJik0KSEiMEExNDk7Pj4+JS5ESUM8SDc9

+ PjsBCgsLDg0OHBAQHDsoIig7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7

+ Ozs7Ozs7Ozs7Ozs7O//AABEIAEgAYAMBIQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAA

+ AQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNC

+ scEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hp

+ anN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS

+ 09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcI

+ CQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVi

+ ctEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4

+ eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY

+ 2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AJ7SLgcVr20I4rNFGvbQAAHFaEUX

+ SrQi5HCMdKk8oY6VSJYx4hjpVaWMelAFC4iGDxWPdR8mkxmRdxjBrEvI+tZjN20xtHNbNqAc

+ UIDXg6Cr0WKtCY8XKQOyzOB3FKNWsyceZ+lS6sY6NkjvPSdwImBHUmmy4q076oCjOODWPdgc

+ 0MpGPdAYNYl4o5rNjNKzkyorZtXxihAa1vIDip7m9Frb7/4jwKcnyxbEzN3ieJppZsyZ4U1H

+ urzZau4mWVlNrGk0UuWPVa1YroXEIkHfrXZh5W90RWncAHmsi6bJNdQ0ZNw3BrGuiMGs2Mks

+ puBzWzbzdOaEBeOpR2oUtkk9hTru7iuo4m8wgemKyqTi04sBsfkEf68j8KlUQZz9o/SuZRj3

+ JYriAji4/Sp7W6htbV2aXcu70ramoxle4gN7HcIXjbis+4k5NdaaauhmVcv1rHuW61DGiG1m

+ 6c1s20/TmgAv5vmj57VKk3+ixnPc1xVV70h9CVJuOtSrL71hFgxzScUkkn+iY/2q1i9xDrGT

+ 9y31pJ5Otd1L+GhMy7mTrWXO2SapjRn28vTmta3nxjmgGOvJd2w1Kkv+ipz/ABGuOoveYdCe

+ ObjrU6y5rlsA8ycUksn+ij/eNaw6iJLNsW59zTJn6816FP4EJmbO+Saz5m602UjIgk4HNadv

+ LwKaBl+MpIMOMipp490SCJeF7CoqQvF2JuRqWQ4YEGrSiQJuKnHrXByMpki73GFBNXIoh9n2

+ SrnnOK6MPTbd3sSwIVF2qMCqkzHmuy1lYRnTHrVGWpZaMKB+BWlbycYoQM0IZDxzV+GU8c1a

+ IYy5Y+dnHatAsfsAHfArmS1mPoh1gT8x9qtk1rQX7tCe5DIapzGtGBQm71SlqGWjnIH6Vowt

+ zmhAy/E3vV6F6tEMuxlWIyAfrVxCCAO1VZEEyYA4AApxNGwyJ+lVJRUsaKMw61SlFQzRAP/Z

+

+X-ATTRIBUTE:Some String

+BDAY:19800101

+GEO:35.6563854,139.6994233

+URL:http://www.example.com/

+REV:20080424T195243Z

+END:VCARD
\ No newline at end of file
diff --git a/core/tests/coretests/res/raw/v21_invalid_comment_line.vcf b/core/tests/coretests/res/raw/v21_invalid_comment_line.vcf
new file mode 100644
index 0000000..f910710
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_invalid_comment_line.vcf
@@ -0,0 +1,10 @@
+BEGIN:vCard

+VERSION:2.1

+UID:357

+N:;Conference Call

+FN:Conference Call

+# This line must be ignored.

+NOTE;ENCODING=QUOTED-PRINTABLE:This is an (sharp ->=

+#<- sharp) example. This message must NOT be ignored.

+# This line must be ignored too.

+END:vCard

diff --git a/core/tests/coretests/res/raw/v21_japanese_1.vcf b/core/tests/coretests/res/raw/v21_japanese_1.vcf
new file mode 100644
index 0000000..d05e2ff
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_japanese_1.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD

+VERSION:2.1

+N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh;;;;

+SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ;;;;

+TEL;PREF;VOICE:0300000000

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_japanese_2.vcf b/core/tests/coretests/res/raw/v21_japanese_2.vcf
new file mode 100644
index 0000000..fa54acb
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_japanese_2.vcf
@@ -0,0 +1,10 @@
+BEGIN:VCARD

+VERSION:2.1

+FN;CHARSET=SHIFT_JIS:ˆÀ“¡ ƒƒCƒh 1

+N;CHARSET=SHIFT_JIS:ˆÀ“¡;ƒƒCƒh1;;;

+SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³;Û²ÄÞ1;;;

+ADR;HOME;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:;=93=8C=8B=9E=93=73=

+=8F=61=92=4A=8B=E6=8D=F7=8B=75=92=AC26-1=83=5A=83=8B=83=8A=83=41=83=93=

+=83=5E=83=8F=81=5B6=8A=4B;;;;150-8512;

+NOTE;CHARSET=SHIFT_JIS;ENCODING=QUOTED-PRINTABLE:=83=81=83=82

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_multiple_entry.vcf b/core/tests/coretests/res/raw/v21_multiple_entry.vcf
new file mode 100644
index 0000000..ebbb19a
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_multiple_entry.vcf
@@ -0,0 +1,33 @@
+BEGIN:VCARD

+VERSION:2.1

+N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh3;;;;

+SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ3;;;;

+TEL;X-NEC-SECRET:9

+TEL;X-NEC-HOTEL:10

+TEL;X-NEC-SCHOOL:11

+TEL;HOME;FAX:12

+END:VCARD

+

+

+BEGIN:VCARD

+VERSION:2.1

+N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh4;;;;

+SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ4;;;;

+TEL;MODEM:13

+TEL;PAGER:14

+TEL;X-NEC-FAMILY:15

+TEL;X-NEC-GIRL:16

+END:VCARD

+

+

+BEGIN:VCARD

+VERSION:2.1

+N;CHARSET=SHIFT_JIS:ˆÀ“¡ƒƒCƒh5;;;;

+SOUND;X-IRMC-N;CHARSET=SHIFT_JIS:±ÝÄÞ³Û²ÄÞ5;;;;

+TEL;X-NEC-BOY:17

+TEL;X-NEC-FRIEND:18

+TEL;X-NEC-PHS:19

+TEL;X-NEC-RESTAURANT:20

+END:VCARD

+

+

diff --git a/core/tests/coretests/res/raw/v21_org_before_title.vcf b/core/tests/coretests/res/raw/v21_org_before_title.vcf
new file mode 100644
index 0000000..8ff1190
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_org_before_title.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD

+VERSION:2.1

+FN:Normal Guy

+ORG:Company;Organization;Devision;Room;Sheet No.

+TITLE:Excellent Janitor

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_pref_handling.vcf b/core/tests/coretests/res/raw/v21_pref_handling.vcf
new file mode 100644
index 0000000..5105310
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_pref_handling.vcf
@@ -0,0 +1,15 @@
+BEGIN:VCARD
+VERSION:2.1
+FN:Smith
+TEL;HOME:1
+TEL;WORK;PREF:2
+TEL;ISDN:3
+EMAIL;PREF;HOME:test@example.com
+EMAIL;CELL;PREF:test2@examination.com
+ORG:Company
+TITLE:Engineer
+ORG:Mystery
+TITLE:Blogger
+ORG:Poetry
+TITLE:Poet
+END:VCARD
diff --git a/core/tests/coretests/res/raw/v21_simple_1.vcf b/core/tests/coretests/res/raw/v21_simple_1.vcf
new file mode 100644
index 0000000..6aabb4c
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_simple_1.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD

+N:Ando;Roid;

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_simple_2.vcf b/core/tests/coretests/res/raw/v21_simple_2.vcf
new file mode 100644
index 0000000..f0d5ab5
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_simple_2.vcf
@@ -0,0 +1,3 @@
+BEGIN:VCARD

+FN:Ando Roid

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_simple_3.vcf b/core/tests/coretests/res/raw/v21_simple_3.vcf
new file mode 100644
index 0000000..beddabb
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_simple_3.vcf
@@ -0,0 +1,4 @@
+BEGIN:VCARD

+N:Ando;Roid;

+FN:Ando Roid

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_title_before_org.vcf b/core/tests/coretests/res/raw/v21_title_before_org.vcf
new file mode 100644
index 0000000..9fdc738
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_title_before_org.vcf
@@ -0,0 +1,6 @@
+BEGIN:VCARD

+VERSION:2.1

+FN:Nice Guy

+TITLE:Cool Title

+ORG:Marverous;Perfect;Great;Good;Bad;Poor

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v21_winmo_65.vcf b/core/tests/coretests/res/raw/v21_winmo_65.vcf
new file mode 100644
index 0000000..f380d0d
--- /dev/null
+++ b/core/tests/coretests/res/raw/v21_winmo_65.vcf
@@ -0,0 +1,10 @@
+BEGIN:VCARD

+VERSION:2.1

+N:Example;;;;

+FN:Example

+ANNIVERSARY;VALUE=DATE:20091010

+AGENT:Invalid line which must be handled correctly.

+X-CLASS:PUBLIC

+X-REDUCTION:

+X-NO:

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v30_comma_separated.vcf b/core/tests/coretests/res/raw/v30_comma_separated.vcf
new file mode 100644
index 0000000..98a7f20
--- /dev/null
+++ b/core/tests/coretests/res/raw/v30_comma_separated.vcf
@@ -0,0 +1,5 @@
+BEGIN:VCARD

+VERSION:3.0

+N:F;G;M;;

+TEL;TYPE=PAGER,WORK,MSG:6101231234@pagersample.com

+END:VCARD

diff --git a/core/tests/coretests/res/raw/v30_simple.vcf b/core/tests/coretests/res/raw/v30_simple.vcf
new file mode 100644
index 0000000..418661f
--- /dev/null
+++ b/core/tests/coretests/res/raw/v30_simple.vcf
@@ -0,0 +1,13 @@
+BEGIN:VCARD

+VERSION:3.0

+FN:And Roid

+N:And;Roid;;;

+ORG:Open;Handset; Alliance

+SORT-STRING:android

+TEL;TYPE=PREF;TYPE=VOICE:0300000000

+CLASS:PUBLIC

+X-GNO:0

+X-GN:group0

+X-REDUCTION:0

+REV:20081031T065854Z

+END:VCARD

diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 05b57e0..807386a 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -100,4 +100,21 @@
     <string name="include_button">I was included!</string>
 
     <string name="view">View</string>
+
+    <string name="layout_five_text_text">S</string>
+    <string name="layout_four_text_text">S</string>
+    <string name="layout_six_text_text">S</string>
+
+    <string name="menu_test">test</string>
+    <string name="metadata_text">text</string>
+
+    <string name="permlab_testGranted">Test Granted</string>
+    <string name="permdesc_testGranted">Used for running unit tests, for
+        testing operations where we have the permission.</string>
+    <string name="permlab_testDenied">Test Denied</string>
+    <string name="permdesc_testDenied">Used for running unit tests, for
+        testing operations where we do not have the permission.</string>
+
+    <string name="searchable_label">SearchManager Test</string>
+    <string name="searchable_hint">A search hint</string>
 </resources>
diff --git a/core/tests/coretests/res/xml/metadata.xml b/core/tests/coretests/res/xml/metadata.xml
new file mode 100644
index 0000000..e352f27
--- /dev/null
+++ b/core/tests/coretests/res/xml/metadata.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<thedata xmlns:android="http://schemas.android.com/apk/res/android"
+    rawText="some raw text"
+    rawColor="#ffffff00"
+    android:color="#f00"
+    android:text="@string/metadata_text"
+
+/>
diff --git a/core/tests/coretests/res/xml/metadata_app.xml b/core/tests/coretests/res/xml/metadata_app.xml
new file mode 100644
index 0000000..c37e6ba
--- /dev/null
+++ b/core/tests/coretests/res/xml/metadata_app.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<thedata xmlns:android="http://schemas.android.com/apk/res/android"
+    rawText="some raw text"
+    rawColor="#ffffff00"
+    android:color="#f00"
+    android:text="@string/metadata_text"
+    appInfo="true"
+
+/>
diff --git a/core/tests/coretests/res/xml/searchable.xml b/core/tests/coretests/res/xml/searchable.xml
new file mode 100644
index 0000000..9d293b5
--- /dev/null
+++ b/core/tests/coretests/res/xml/searchable.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<searchable xmlns:android="http://schemas.android.com/apk/res/android"
+    android:label="@string/searchable_label"
+    android:hint="@string/searchable_hint"
+    android:searchSuggestAuthority="com.android.unit_tests.SuggestionProvider"
+    >
+
+        <actionkey android:keycode="KEYCODE_CALL"
+            android:suggestActionMsgColumn="suggest_action_msg_call" />
+
+</searchable>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
new file mode 100644
index 0000000..394b9f2
--- /dev/null
+++ b/core/tests/coretests/src/android/accounts/AccountManagerServiceTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.accounts;
+
+import android.test.AndroidTestCase;
+import android.test.RenamingDelegatingContext;
+import android.test.IsolatedContext;
+import android.test.mock.MockContext;
+import android.test.mock.MockContentResolver;
+import android.content.*;
+import android.accounts.Account;
+import android.accounts.AccountManagerService;
+import android.os.Bundle;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class AccountManagerServiceTest extends AndroidTestCase {
+    @Override
+    protected void setUp() throws Exception {
+        final String filenamePrefix = "test.";
+        MockContentResolver resolver = new MockContentResolver();
+        RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
+                new MockContext(), // The context that most methods are delegated to
+                getContext(), // The context that file methods are delegated to
+                filenamePrefix);
+        Context context = new IsolatedContext(resolver, targetContextWrapper);
+        setContext(context);
+    }
+
+    public class AccountSorter implements Comparator<Account> {
+        public int compare(Account object1, Account object2) {
+            if (object1 == object2) return 0;
+            if (object1 == null) return 1;
+            if (object2 == null) return -1;
+            int result = object1.type.compareTo(object2.type);
+            if (result != 0) return result;
+            return object1.name.compareTo(object2.name);
+        }
+    }
+
+    public void testCheckAddAccount() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Account a21 = new Account("account2", "type1");
+        Account a31 = new Account("account3", "type1");
+        Account a12 = new Account("account1", "type2");
+        Account a22 = new Account("account2", "type2");
+        Account a32 = new Account("account3", "type2");
+        ams.addAccount(a11, "p11", null);
+        ams.addAccount(a12, "p12", null);
+        ams.addAccount(a21, "p21", null);
+        ams.addAccount(a22, "p22", null);
+        ams.addAccount(a31, "p31", null);
+        ams.addAccount(a32, "p32", null);
+
+        Account[] accounts = ams.getAccounts(null);
+        Arrays.sort(accounts, new AccountSorter());
+        assertEquals(6, accounts.length);
+        assertEquals(a11, accounts[0]);
+        assertEquals(a21, accounts[1]);
+        assertEquals(a31, accounts[2]);
+        assertEquals(a12, accounts[3]);
+        assertEquals(a22, accounts[4]);
+        assertEquals(a32, accounts[5]);
+
+        accounts = ams.getAccountsByType("type1" );
+        Arrays.sort(accounts, new AccountSorter());
+        assertEquals(3, accounts.length);
+        assertEquals(a11, accounts[0]);
+        assertEquals(a21, accounts[1]);
+        assertEquals(a31, accounts[2]);
+
+        ams.removeAccount(null, a21);
+
+        accounts = ams.getAccountsByType("type1" );
+        Arrays.sort(accounts, new AccountSorter());
+        assertEquals(2, accounts.length);
+        assertEquals(a11, accounts[0]);
+        assertEquals(a31, accounts[1]);
+    }
+
+    public void testPasswords() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Account a12 = new Account("account1", "type2");
+        ams.addAccount(a11, "p11", null);
+        ams.addAccount(a12, "p12", null);
+
+        assertEquals("p11", ams.getPassword(a11));
+        assertEquals("p12", ams.getPassword(a12));
+
+        ams.setPassword(a11, "p11b");
+
+        assertEquals("p11b", ams.getPassword(a11));
+        assertEquals("p12", ams.getPassword(a12));
+    }
+
+    public void testUserdata() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Bundle u11 = new Bundle();
+        u11.putString("a", "a_a11");
+        u11.putString("b", "b_a11");
+        u11.putString("c", "c_a11");
+        Account a12 = new Account("account1", "type2");
+        Bundle u12 = new Bundle();
+        u12.putString("a", "a_a12");
+        u12.putString("b", "b_a12");
+        u12.putString("c", "c_a12");
+        ams.addAccount(a11, "p11", u11);
+        ams.addAccount(a12, "p12", u12);
+
+        assertEquals("a_a11", ams.getUserData(a11, "a"));
+        assertEquals("b_a11", ams.getUserData(a11, "b"));
+        assertEquals("c_a11", ams.getUserData(a11, "c"));
+        assertEquals("a_a12", ams.getUserData(a12, "a"));
+        assertEquals("b_a12", ams.getUserData(a12, "b"));
+        assertEquals("c_a12", ams.getUserData(a12, "c"));
+
+        ams.setUserData(a11, "b", "b_a11b");
+
+        assertEquals("a_a11", ams.getUserData(a11, "a"));
+        assertEquals("b_a11b", ams.getUserData(a11, "b"));
+        assertEquals("c_a11", ams.getUserData(a11, "c"));
+        assertEquals("a_a12", ams.getUserData(a12, "a"));
+        assertEquals("b_a12", ams.getUserData(a12, "b"));
+        assertEquals("c_a12", ams.getUserData(a12, "c"));
+    }
+
+    public void testAuthtokens() throws Exception {
+        AccountManagerService ams = new AccountManagerService(getContext());
+        Account a11 = new Account("account1", "type1");
+        Account a12 = new Account("account1", "type2");
+        ams.addAccount(a11, "p11", null);
+        ams.addAccount(a12, "p12", null);
+
+        ams.setAuthToken(a11, "att1", "a11_att1");
+        ams.setAuthToken(a11, "att2", "a11_att2");
+        ams.setAuthToken(a11, "att3", "a11_att3");
+        ams.setAuthToken(a12, "att1", "a12_att1");
+        ams.setAuthToken(a12, "att2", "a12_att2");
+        ams.setAuthToken(a12, "att3", "a12_att3");
+
+        assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+        assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+        assertEquals("a11_att3", ams.peekAuthToken(a11, "att3"));
+        assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+        assertEquals("a12_att2", ams.peekAuthToken(a12, "att2"));
+        assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+        ams.setAuthToken(a11, "att3", "a11_att3b");
+        ams.invalidateAuthToken(a12.type, "a12_att2");
+
+        assertEquals("a11_att1", ams.peekAuthToken(a11, "att1"));
+        assertEquals("a11_att2", ams.peekAuthToken(a11, "att2"));
+        assertEquals("a11_att3b", ams.peekAuthToken(a11, "att3"));
+        assertEquals("a12_att1", ams.peekAuthToken(a12, "att1"));
+        assertNull(ams.peekAuthToken(a12, "att2"));
+        assertEquals("a12_att3", ams.peekAuthToken(a12, "att3"));
+
+        assertNull(ams.readAuthTokenFromDatabase(a12, "att2"));
+    }
+}
diff --git a/core/tests/coretests/src/android/app/SearchManagerTest.java b/core/tests/coretests/src/android/app/SearchManagerTest.java
new file mode 100644
index 0000000..21ed4c5
--- /dev/null
+++ b/core/tests/coretests/src/android/app/SearchManagerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.app.activity.LocalActivity;
+
+import android.app.Activity;
+import android.app.ISearchManager;
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+
+/**
+ * To launch this test from the command line:
+ * 
+ * adb shell am instrument -w \
+ *   -e class com.android.unit_tests.SearchManagerTest \
+ *   com.android.unit_tests/android.test.InstrumentationTestRunner
+ */
+public class SearchManagerTest extends ActivityInstrumentationTestCase2<LocalActivity> {
+
+    private ComponentName SEARCHABLE_ACTIVITY =
+            new ComponentName("com.android.frameworks.coretests",
+                    "android.app.activity.SearchableActivity");
+
+    /*
+     * Bug list of test ideas.
+     * 
+     * testSearchManagerInterfaceAvailable()
+     *  Exercise the interface obtained
+     *  
+     * testSearchManagerAvailable()
+     *  Exercise the interface obtained
+     *  
+     * testSearchManagerInvocations()
+     *  FIX - make it work again
+     *  stress test with a very long string
+     * 
+     * SearchManager tests
+     *  confirm proper identification of "default" activity based on policy, not hardcoded contacts
+     *  
+     * SearchBar tests
+     *  Maybe have to do with framework / unittest runner - need instrumented activity?
+     *  How can we unit test the suggestions content providers?
+     *  Should I write unit tests for any of them?
+     *  Test scenarios:
+     *    type-BACK (cancel)
+     *    type-GO (send)
+     *    type-navigate-click (suggestion)
+     *    type-action
+     *    type-navigate-action (suggestion)
+     */
+    
+    /**
+     * Local copy of activity context
+     */
+    Context mContext;
+
+    public SearchManagerTest() {
+        super("com.android.frameworks.coretests", LocalActivity.class);
+    }
+
+    /**
+     * Setup any common data for the upcoming tests.
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        
+        Activity testActivity = getActivity();
+        mContext = testActivity;
+    }
+
+    private ISearchManager getSearchManagerService() {
+        return ISearchManager.Stub.asInterface(
+                ServiceManager.getService(Context.SEARCH_SERVICE));
+    }
+
+    /**
+     * The goal of this test is to confirm that we can obtain
+     * a search manager interface.
+     */
+    @MediumTest
+    public void testSearchManagerInterfaceAvailable() {
+        assertNotNull(getSearchManagerService());
+    }
+    
+    /**
+     * The goal of this test is to confirm that we can obtain
+     * a search manager at any time, and that for any given context,
+     * it is a singleton.
+     */
+    @LargeTest
+    public void testSearchManagerAvailable() {
+        SearchManager searchManager1 = (SearchManager)
+                mContext.getSystemService(Context.SEARCH_SERVICE);
+        assertNotNull(searchManager1);
+        SearchManager searchManager2 = (SearchManager)
+                mContext.getSystemService(Context.SEARCH_SERVICE);
+        assertNotNull(searchManager2);
+        assertSame(searchManager1, searchManager2 );
+    }
+
+    @MediumTest
+    public void testSearchables() {
+        SearchManager searchManager = (SearchManager)
+                mContext.getSystemService(Context.SEARCH_SERVICE);
+        SearchableInfo si;
+
+        si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, false);
+        assertNotNull(si);
+        assertFalse(searchManager.isDefaultSearchable(si));
+        si = searchManager.getSearchableInfo(SEARCHABLE_ACTIVITY, true);
+        assertNotNull(si);
+        assertTrue(searchManager.isDefaultSearchable(si));
+        si = searchManager.getSearchableInfo(null, true);
+        assertNotNull(si);
+        assertTrue(searchManager.isDefaultSearchable(si));
+    }
+
+    /**
+     * Tests that startSearch() can be called multiple times without stopSearch()
+     * in between.
+     */
+    @MediumTest
+    public void testStartSearchIdempotent() throws Exception {
+         SearchManager searchManager = (SearchManager)
+                 mContext.getSystemService(Context.SEARCH_SERVICE);
+         assertNotNull(searchManager);
+
+         searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+         searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+         searchManager.stopSearch();
+    }
+
+    /**
+     * Tests that stopSearch() can be called when the search UI is not visible and can be
+     * called multiple times without startSearch() in between.
+     */
+    @MediumTest
+    public void testStopSearchIdempotent() throws Exception {
+         SearchManager searchManager = (SearchManager)
+                 mContext.getSystemService(Context.SEARCH_SERVICE);
+         assertNotNull(searchManager);
+         searchManager.stopSearch();
+
+         searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+         searchManager.stopSearch();
+         searchManager.stopSearch();
+    }
+
+    /**
+     * The goal of this test is to confirm that we can start and then
+     * stop a simple search.
+     */
+    @MediumTest
+    public void testSearchManagerInvocations() throws Exception {
+        SearchManager searchManager = (SearchManager)
+                mContext.getSystemService(Context.SEARCH_SERVICE);
+        assertNotNull(searchManager);
+
+        // These tests should simply run to completion w/o exceptions
+        searchManager.startSearch(null, false, SEARCHABLE_ACTIVITY, null, false);
+        searchManager.stopSearch();
+
+        searchManager.startSearch("", false, SEARCHABLE_ACTIVITY, null, false);
+        searchManager.stopSearch();
+
+        searchManager.startSearch("test search string", false, SEARCHABLE_ACTIVITY, null, false);
+        searchManager.stopSearch();
+
+        searchManager.startSearch("test search string", true, SEARCHABLE_ACTIVITY, null, false);
+        searchManager.stopSearch();
+    }
+
+}
diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java
new file mode 100644
index 0000000..9b4520e
--- /dev/null
+++ b/core/tests/coretests/src/android/app/SearchablesTest.java
@@ -0,0 +1,477 @@
+/*
+ * 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.app;
+
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.app.SearchableInfo.ActionKeyInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.os.RemoteException;
+import android.server.search.Searchables;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.mock.MockContext;
+import android.test.mock.MockPackageManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * To launch this test from the command line:
+ * 
+ * adb shell am instrument -w \
+ *   -e class com.android.unit_tests.SearchablesTest \
+ *   com.android.unit_tests/android.test.InstrumentationTestRunner
+ */
+@SmallTest
+public class SearchablesTest extends AndroidTestCase {
+    
+    /*
+     * SearchableInfo tests
+     *  Mock the context so I can provide very specific input data
+     *  Confirm OK with "zero" searchables
+     *  Confirm "good" metadata read properly
+     *  Confirm "bad" metadata skipped properly
+     *  Confirm ordering of searchables
+     *  Confirm "good" actionkeys
+     *  confirm "bad" actionkeys are rejected
+     *  confirm XML ordering enforced (will fail today - bug in SearchableInfo)
+     *  findActionKey works
+     *  getIcon works
+     */
+    
+    /**
+     * The goal of this test is to confirm proper operation of the 
+     * SearchableInfo helper class.
+     * 
+     * TODO:  The metadata source needs to be mocked out because adding
+     * searchability metadata via this test is causing it to leak into the
+     * real system.  So for now I'm just going to test for existence of the
+     * GlobalSearch app (which is searchable).
+     */
+    public void testSearchableGlobalSearch() {
+        // test basic array & hashmap
+        Searchables searchables = new Searchables(mContext);
+        searchables.buildSearchableList();
+
+        // test linkage from another activity
+        // TODO inject this via mocking into the package manager.
+        // TODO for now, just check for searchable GlobalSearch app (this isn't really a unit test)
+        ComponentName thisActivity = new ComponentName(
+                "com.android.globalsearch",
+                "com.android.globalsearch.GlobalSearch");
+
+        SearchableInfo si = searchables.getSearchableInfo(thisActivity);
+        assertNotNull(si);
+        assertEquals(thisActivity, si.getSearchActivity());
+        
+        Context appContext = si.getActivityContext(mContext);
+        assertNotNull(appContext);
+        MoreAsserts.assertNotEqual(appContext, mContext);
+        assertEquals("Quick Search Box", appContext.getString(si.getHintId()));
+        assertEquals("Quick Search Box", appContext.getString(si.getLabelId()));
+    }
+    
+    /**
+     * Test that non-searchable activities return no searchable info (this would typically
+     * trigger the use of the default searchable e.g. contacts)
+     */
+    public void testNonSearchable() {
+        // test basic array & hashmap
+        Searchables searchables = new Searchables(mContext);
+        searchables.buildSearchableList();
+
+        // confirm that we return null for non-searchy activities
+        ComponentName nonActivity = new ComponentName(
+                            "com.android.frameworks.coretests",
+                            "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY");
+        SearchableInfo si = searchables.getSearchableInfo(nonActivity);
+        assertNull(si);
+    }
+    
+    /**
+     * Test that there is a default searchable (aka global search provider).
+     */
+    public void testDefaultSearchable() {
+        Searchables searchables = new Searchables(mContext);
+        searchables.buildSearchableList();
+        SearchableInfo si = searchables.getDefaultSearchable();
+        checkSearchable(si);
+        assertTrue(searchables.isDefaultSearchable(si));
+    }
+    
+    /**
+     * This is an attempt to run the searchable info list with a mocked context.  Here are some
+     * things I'd like to test.
+     *
+     *  Confirm OK with "zero" searchables
+     *  Confirm "good" metadata read properly
+     *  Confirm "bad" metadata skipped properly
+     *  Confirm ordering of searchables
+     *  Confirm "good" actionkeys
+     *  confirm "bad" actionkeys are rejected
+     *  confirm XML ordering enforced (will fail today - bug in SearchableInfo)
+     *  findActionKey works
+     *  getIcon works
+
+     */
+    public void testSearchablesListReal() {
+        MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
+        MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+
+        // build item list with real-world source data
+        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
+        Searchables searchables = new Searchables(mockContext);
+        searchables.buildSearchableList();
+        // tests with "real" searchables (deprecate, this should be a unit test)
+        ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
+        int count = searchablesList.size();
+        assertTrue(count >= 1);         // this isn't really a unit test
+        checkSearchables(searchablesList);
+        ArrayList<SearchableInfo> global = searchables.getSearchablesInGlobalSearchList();
+        checkSearchables(global);
+    }
+
+    /**
+     * This round of tests confirms good operations with "zero" searchables found
+     */
+    public void testSearchablesListEmpty() {
+        MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
+        MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+
+        mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
+        Searchables searchables = new Searchables(mockContext);
+        searchables.buildSearchableList();
+        ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
+        assertNotNull(searchablesList);
+        MoreAsserts.assertEmpty(searchablesList);
+        ArrayList<SearchableInfo> global = searchables.getSearchablesInGlobalSearchList();
+        MoreAsserts.assertEmpty(global);
+    }
+    
+    /**
+     * Generic health checker for an array of searchables.
+     * 
+     * This is designed to pass for any semi-legal searchable, without knowing much about
+     * the format of the underlying data.  It's fairly easy for a non-compliant application
+     * to provide meta-data that will pass here (e.g. a non-existent suggestions authority).
+     * 
+     * @param searchables The list of searchables to examine.
+     */
+    private void checkSearchables(ArrayList<SearchableInfo> searchablesList) {
+        assertNotNull(searchablesList);
+        int count = searchablesList.size();
+        for (int ii = 0; ii < count; ii++) {
+            SearchableInfo si = searchablesList.get(ii);
+            checkSearchable(si);
+        }
+    }
+    
+    private void checkSearchable(SearchableInfo si) {
+        assertNotNull(si);
+        assertTrue(si.getLabelId() != 0);        // This must be a useable string
+        assertNotEmpty(si.getSearchActivity().getClassName());
+        assertNotEmpty(si.getSearchActivity().getPackageName());
+        if (si.getSuggestAuthority() != null) {
+            // The suggestion fields are largely optional, so we'll just confirm basic health
+            assertNotEmpty(si.getSuggestAuthority());
+            assertNullOrNotEmpty(si.getSuggestPath());
+            assertNullOrNotEmpty(si.getSuggestSelection());
+            assertNullOrNotEmpty(si.getSuggestIntentAction());
+            assertNullOrNotEmpty(si.getSuggestIntentData());
+        }
+        /* Add a way to get the entire action key list, then explicitly test its elements */
+        /* For now, test the most common action key (CALL) */
+        ActionKeyInfo ai = si.findActionKey(KeyEvent.KEYCODE_CALL);
+        if (ai != null) {
+            assertEquals(ai.getKeyCode(), KeyEvent.KEYCODE_CALL);
+            // one of these three fields must be non-null & non-empty
+            boolean m1 = (ai.getQueryActionMsg() != null) && (ai.getQueryActionMsg().length() > 0);
+            boolean m2 = (ai.getSuggestActionMsg() != null) && (ai.getSuggestActionMsg().length() > 0);
+            boolean m3 = (ai.getSuggestActionMsgColumn() != null) && 
+                            (ai.getSuggestActionMsgColumn().length() > 0);
+            assertTrue(m1 || m2 || m3);
+        }
+        
+        /* 
+         * Find ways to test these:
+         * 
+         * private int mSearchMode
+         * private Drawable mIcon
+         */
+        
+        /*
+         * Explicitly not tested here:
+         * 
+         * Can be null, so not much to see:
+         * public String mSearchHint
+         * private String mZeroQueryBanner
+         * 
+         * To be deprecated/removed, so don't bother:
+         * public boolean mFilterMode
+         * public boolean mQuickStart
+         * private boolean mIconResized
+         * private int mIconResizeWidth
+         * private int mIconResizeHeight
+         * 
+         * All of these are "internal" working variables, not part of any contract
+         * private ActivityInfo mActivityInfo
+         * private Rect mTempRect
+         * private String mSuggestProviderPackage
+         * private String mCacheActivityContext
+         */
+    }
+    
+    /**
+     * Combo assert for "string not null and not empty"
+     */
+    private void assertNotEmpty(final String s) {
+        assertNotNull(s);
+        MoreAsserts.assertNotEqual(s, "");
+    }
+    
+    /**
+     * Combo assert for "string null or (not null and not empty)"
+     */
+    private void assertNullOrNotEmpty(final String s) {
+        if (s != null) {
+            MoreAsserts.assertNotEqual(s, "");
+        }
+    }    
+    
+    /**
+     * This is a mock for context.  Used to perform a true unit test on SearchableInfo.
+     * 
+     */
+    private class MyMockContext extends MockContext {
+        
+        protected Context mRealContext;
+        protected PackageManager mPackageManager;
+        
+        /**
+         * Constructor.
+         * 
+         * @param realContext Please pass in a real context for some pass-throughs to function.
+         */
+        MyMockContext(Context realContext, PackageManager packageManager) {
+            mRealContext = realContext;
+            mPackageManager = packageManager;
+        }
+        
+        /**
+         * Resources.  Pass through for now.
+         */
+        @Override
+        public Resources getResources() {
+            return mRealContext.getResources();
+        }
+
+        /**
+         * Package manager.  Pass through for now.
+         */
+        @Override
+        public PackageManager getPackageManager() {
+            return mPackageManager;
+        }
+
+        /**
+         * Package manager.  Pass through for now.
+         */
+        @Override
+        public Context createPackageContext(String packageName, int flags)
+                throws PackageManager.NameNotFoundException {
+            return mRealContext.createPackageContext(packageName, flags);
+        }
+
+        /**
+         * Message broadcast.  Pass through for now.
+         */
+        @Override
+        public void sendBroadcast(Intent intent) {
+            mRealContext.sendBroadcast(intent);
+        }
+    }
+
+/**
+ * This is a mock for package manager.  Used to perform a true unit test on SearchableInfo.
+ * 
+ */
+    private class MyMockPackageManager extends MockPackageManager {
+        
+        public final static int SEARCHABLES_PASSTHROUGH = 0;
+        public final static int SEARCHABLES_MOCK_ZERO = 1;
+        public final static int SEARCHABLES_MOCK_ONEGOOD = 2;
+        public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3;
+        
+        protected PackageManager mRealPackageManager;
+        protected int mSearchablesMode;
+
+        public MyMockPackageManager(PackageManager realPM) {
+            mRealPackageManager = realPM;
+            mSearchablesMode = SEARCHABLES_PASSTHROUGH;
+        }
+
+        /**
+         * Set the mode for various tests.
+         */
+        public void setSearchablesMode(int newMode) {
+            switch (newMode) {
+            case SEARCHABLES_PASSTHROUGH:
+            case SEARCHABLES_MOCK_ZERO:
+                mSearchablesMode = newMode;
+                break;
+                
+            default:
+                throw new UnsupportedOperationException();       
+            }
+        }
+        
+        /**
+         * Find activities that support a given intent.
+         * 
+         * Retrieve all activities that can be performed for the given intent.
+         * 
+         * @param intent The desired intent as per resolveActivity().
+         * @param flags Additional option flags.  The most important is
+         *                    MATCH_DEFAULT_ONLY, to limit the resolution to only
+         *                    those activities that support the CATEGORY_DEFAULT.
+         * 
+         * @return A List<ResolveInfo> containing one entry for each matching
+         *         Activity. These are ordered from best to worst match -- that
+         *         is, the first item in the list is what is returned by
+         *         resolveActivity().  If there are no matching activities, an empty
+         *         list is returned.
+         */
+        @Override 
+        public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+            assertNotNull(intent);
+            assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
+                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH));
+            switch (mSearchablesMode) {
+            case SEARCHABLES_PASSTHROUGH:
+                return mRealPackageManager.queryIntentActivities(intent, flags);
+            case SEARCHABLES_MOCK_ZERO:
+                return null;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        }
+        
+        @Override
+        public ResolveInfo resolveActivity(Intent intent, int flags) {
+            assertNotNull(intent);
+            assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
+                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
+            switch (mSearchablesMode) {
+            case SEARCHABLES_PASSTHROUGH:
+                return mRealPackageManager.resolveActivity(intent, flags);
+            case SEARCHABLES_MOCK_ZERO:
+                return null;
+            default:
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        /**
+         * Retrieve an XML file from a package.  This is a low-level API used to
+         * retrieve XML meta data.
+         * 
+         * @param packageName The name of the package that this xml is coming from.
+         * Can not be null.
+         * @param resid The resource identifier of the desired xml.  Can not be 0.
+         * @param appInfo Overall information about <var>packageName</var>.  This
+         * may be null, in which case the application information will be retrieved
+         * for you if needed; if you already have this information around, it can
+         * be much more efficient to supply it here.
+         * 
+         * @return Returns an XmlPullParser allowing you to parse out the XML
+         * data.  Returns null if the xml resource could not be found for any
+         * reason.
+         */
+        @Override 
+        public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) {
+            assertNotNull(packageName);
+            MoreAsserts.assertNotEqual(packageName, "");
+            MoreAsserts.assertNotEqual(resid, 0);
+            switch (mSearchablesMode) {
+            case SEARCHABLES_PASSTHROUGH:
+                return mRealPackageManager.getXml(packageName, resid, appInfo);
+            case SEARCHABLES_MOCK_ZERO:
+            default:
+                throw new UnsupportedOperationException();
+            }
+        }
+        
+        /**
+         * Find a single content provider by its base path name.
+         * 
+         * @param name The name of the provider to find.
+         * @param flags Additional option flags.  Currently should always be 0.
+         * 
+         * @return ContentProviderInfo Information about the provider, if found,
+         *         else null.
+         */
+        @Override 
+        public ProviderInfo resolveContentProvider(String name, int flags) {
+            assertNotNull(name);
+            MoreAsserts.assertNotEqual(name, "");
+            assertEquals(flags, 0);
+            switch (mSearchablesMode) {
+            case SEARCHABLES_PASSTHROUGH:
+                return mRealPackageManager.resolveContentProvider(name, flags);
+            case SEARCHABLES_MOCK_ZERO:
+            default:
+                throw new UnsupportedOperationException();
+            }
+        }
+
+        /**
+         * Get the activity information for a particular activity.
+         *
+         * @param name The name of the activity to find.
+         * @param flags Additional option flags.
+         *
+         * @return ActivityInfo Information about the activity, if found, else null.
+         */
+        @Override
+        public ActivityInfo getActivityInfo(ComponentName name, int flags)
+                throws NameNotFoundException {
+            assertNotNull(name);
+            MoreAsserts.assertNotEqual(name, "");
+            switch (mSearchablesMode) {
+            case SEARCHABLES_PASSTHROUGH:
+                return mRealPackageManager.getActivityInfo(name, flags);
+            case SEARCHABLES_MOCK_ZERO:
+                throw new NameNotFoundException();
+            default:
+                throw new UnsupportedOperationException();
+            }
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/SuggestionProvider.java b/core/tests/coretests/src/android/app/SuggestionProvider.java
new file mode 100644
index 0000000..9fb7dcf
--- /dev/null
+++ b/core/tests/coretests/src/android/app/SuggestionProvider.java
@@ -0,0 +1,110 @@
+/*
+ * 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.app;
+
+import android.app.SearchManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+/** Simple test provider that runs in the local process.
+ *
+ * Used by {@link SearchManagerTest}.
+ */
+public class SuggestionProvider extends ContentProvider {
+    private static final String TAG = "SuggestionProvider";
+
+    private static final int SEARCH_SUGGESTIONS = 1;
+
+    private static final UriMatcher sURLMatcher = new UriMatcher(
+            UriMatcher.NO_MATCH);
+
+    static {
+        sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY,
+                SEARCH_SUGGESTIONS);
+        sURLMatcher.addURI("*", SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
+                SEARCH_SUGGESTIONS);
+    }
+
+    private static final String[] COLUMNS = new String[] {
+            "_id",
+            SearchManager.SUGGEST_COLUMN_TEXT_1,
+            SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+            SearchManager.SUGGEST_COLUMN_QUERY
+    };
+
+    public SuggestionProvider() {
+    }
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri url, String[] projectionIn, String selection,
+            String[] selectionArgs, String sort) {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case SEARCH_SUGGESTIONS:
+                String query = url.getLastPathSegment();
+                MatrixCursor cursor = new MatrixCursor(COLUMNS);
+                String[] suffixes = { "", "a", " foo", "XXXXXXXXXXXXXXXXX" };
+                for (String suffix : suffixes) {
+                    addRow(cursor, query + suffix);
+                }
+                return cursor;
+            default:
+                throw new IllegalArgumentException("Unknown URL: " + url);
+        }
+    }
+
+    private void addRow(MatrixCursor cursor, String string) {
+        long id = cursor.getCount();
+        cursor.newRow().add(id).add(string).add(Intent.ACTION_SEARCH).add(string);
+    }
+
+    @Override
+    public String getType(Uri url) {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case SEARCH_SUGGESTIONS:
+                return SearchManager.SUGGEST_MIME_TYPE;
+            default:
+                throw new IllegalArgumentException("Unknown URL: " + url);
+        }
+    }
+
+    @Override
+    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+        throw new UnsupportedOperationException("update not supported");
+    }
+
+    @Override
+    public Uri insert(Uri url, ContentValues initialValues) {
+        throw new UnsupportedOperationException("insert not supported");
+    }
+
+    @Override
+    public int delete(Uri url, String where, String[] whereArgs) {
+        throw new UnsupportedOperationException("delete not supported");
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/AbortReceiver.java b/core/tests/coretests/src/android/app/activity/AbortReceiver.java
new file mode 100644
index 0000000..fef1775
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/AbortReceiver.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.app.activity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.util.Log;
+
+public class AbortReceiver extends BroadcastReceiver
+{
+    public AbortReceiver()
+    {
+    }
+
+    public void onReceive(Context context, Intent intent)
+    {
+	//Log.i("AbortReceiver", "onReceiveIntent!");
+        try {
+            IBinder caller = intent.getIBinderExtra("caller");
+            Parcel data = Parcel.obtain();
+            data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+            data.writeString(LaunchpadActivity.RECEIVER_ABORT);
+            caller.transact(LaunchpadActivity.GOT_RECEIVE_TRANSACTION, data, null, 0);
+            data.recycle();
+        } catch (RemoteException ex) {
+        }
+        
+        // abort the broadcast!!!
+        abortBroadcast();
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
new file mode 100644
index 0000000..61d73bc
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.activity;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.content.res.Configuration;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class ActivityManagerTest extends AndroidTestCase {
+
+    protected Context mContext;
+    protected ActivityManager mActivityManager;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getContext();
+        mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+    }
+
+    // TODO should write a test for getRecentTasks()
+    // TODO should write a test for getRunningTasks()
+    // TODO should write a test for getMemoryInfo()
+    
+    // TODO: Find a way to re-enable this.  It fails if any other app has failed during startup.
+    // This is probably an OK assumption given the desired system status when we run unit tests,
+    // but it's not necessarily the right assumption for a unit test.
+    @Suppress
+    public void disabledTestErrorTasksEmpty() throws Exception {
+        
+        List<ActivityManager.ProcessErrorStateInfo> errList;
+        
+        errList = mActivityManager.getProcessesInErrorState();
+        
+        // test: confirm list is empty
+        assertNull(errList);
+    }
+    
+    // TODO: Force an activity into an error state - then see if we can catch it here?
+    @SmallTest
+    public void testErrorTasksWithError() throws Exception {
+        
+        List<ActivityManager.ProcessErrorStateInfo> errList;
+        
+        // TODO force another process into an error condition.  How?
+        
+        // test: confirm error list length is at least 1 under varying query lengths
+//      checkErrorListMax(1,-1);
+
+        errList = mActivityManager.getProcessesInErrorState();
+
+        // test: the list itself is healthy
+        checkErrorListSanity(errList);
+
+        // test: confirm our application shows up in the list
+    }
+    
+    // TODO: Force an activity into an ANR state - then see if we can catch it here?
+    @SmallTest
+    public void testErrorTasksWithANR() throws Exception {
+        
+        List<ActivityManager.ProcessErrorStateInfo> errList;
+        
+        // TODO: force an application into an ANR state
+        
+        errList = mActivityManager.getProcessesInErrorState();
+
+        // test: the list itself is healthy
+        checkErrorListSanity(errList);
+
+        // test: confirm our ANR'ing application shows up in the list
+    }
+    
+    @SmallTest
+    public void testGetDeviceConfigurationInfo() throws Exception {
+        ConfigurationInfo config = mActivityManager.getDeviceConfigurationInfo();
+        assertNotNull(config);
+        // Validate values against configuration retrieved from resources
+        Configuration vconfig = mContext.getResources().getConfiguration();
+        assertNotNull(vconfig);
+        assertEquals(config.reqKeyboardType, vconfig.keyboard);
+        assertEquals(config.reqTouchScreen, vconfig.touchscreen);
+        assertEquals(config.reqNavigation, vconfig.navigation);
+        if (vconfig.navigation == Configuration.NAVIGATION_NONAV) {
+            assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV);
+        }
+        if (vconfig.keyboard != Configuration.KEYBOARD_UNDEFINED) {
+            assertNotNull(config.reqInputFeatures & ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD);
+        }    
+    }
+    
+    // If any entries in appear in the list, sanity check them against all running applications
+    private void checkErrorListSanity(List<ActivityManager.ProcessErrorStateInfo> errList) {
+        if (errList == null) return;
+        
+        Iterator<ActivityManager.ProcessErrorStateInfo> iter = errList.iterator();
+        while (iter.hasNext()) {
+            ActivityManager.ProcessErrorStateInfo info = iter.next();
+            assertNotNull(info);
+            // sanity checks
+            assertTrue((info.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) ||
+                       (info.condition == ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING));
+            // TODO look at each of these and consider a stronger test
+            // TODO can we cross-check at the process name via some other API?
+            // TODO is there a better test for strings, e.g. "assertIsLegalString")
+            assertNotNull(info.processName);
+            // reasonableness test for info.pid ?
+            assertNotNull(info.longMsg);
+            assertNotNull(info.shortMsg);
+            // is there any reasonable test for the crashData?  Probably not. 
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/ActivityTests.java b/core/tests/coretests/src/android/app/activity/ActivityTests.java
new file mode 100644
index 0000000..c57fe98
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import junit.framework.TestSuite;
+
+public class ActivityTests {
+    public static final boolean DEBUG_LIFECYCLE = false;
+    
+    public static TestSuite suite() {
+        TestSuite suite = new TestSuite(ActivityTests.class.getName());
+
+        suite.addTestSuite(BroadcastTest.class);
+        suite.addTestSuite(IntentSenderTest.class);
+        suite.addTestSuite(ActivityManagerTest.class);
+        suite.addTestSuite(LaunchTest.class);
+        suite.addTestSuite(LifecycleTest.class);
+        suite.addTestSuite(ServiceTest.class);
+        suite.addTestSuite(MetaDataTest.class);
+        // Remove temporarily until bug 1171309 is fixed.
+        //suite.addTestSuite(SubActivityTest.class);
+        suite.addTestSuite(SetTimeZonePermissionsTest.class);
+        
+        return suite;
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityTestsBase.java b/core/tests/coretests/src/android/app/activity/ActivityTestsBase.java
new file mode 100644
index 0000000..232abe2
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ActivityTestsBase.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+
+public class ActivityTestsBase extends AndroidTestCase 
+        implements PerformanceTestCase, LaunchpadActivity.CallingTest {
+    public static final String PERMISSION_GRANTED =
+            "com.android.frameworks.coretests.permission.TEST_GRANTED";
+    public static final String PERMISSION_DENIED =
+            "com.android.frameworks.coretests.permission.TEST_DENIED";
+
+    protected Intent mIntent;
+
+    private PerformanceTestCase.Intermediates mIntermediates;
+    private String mExpecting;
+
+    // Synchronization of activity result.
+    private boolean mFinished;
+    private int mResultCode = 0;
+    private Intent mData;
+    private RuntimeException mResultStack = null;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mIntent = new Intent(mContext, LaunchpadActivity.class);
+        mIntermediates = null;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mIntermediates = null;
+        super.tearDown();
+    }
+
+    public boolean isPerformanceOnly() {
+        return false;
+    }
+
+    public void setInternalIterations(int count) {
+    }
+
+    public void startTiming(boolean realTime) {
+        if (mIntermediates != null) {
+            mIntermediates.startTiming(realTime);
+        }
+    }
+
+    public void addIntermediate(String name) {
+        if (mIntermediates != null) {
+            mIntermediates.addIntermediate(name);
+        }
+    }
+
+    public void addIntermediate(String name, long timeInNS) {
+        if (mIntermediates != null) {
+            mIntermediates.addIntermediate(name, timeInNS);
+        }
+    }
+
+    public void finishTiming(boolean realTime) {
+        if (mIntermediates != null) {
+            mIntermediates.finishTiming(realTime);
+        }
+    }
+
+    public void activityFinished(int resultCode, Intent data, RuntimeException where) {
+        finishWithResult(resultCode, data, where);
+    }
+
+    public Intent editIntent() {
+        return mIntent;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    public int startPerformance(Intermediates intermediates) {
+        mIntermediates = intermediates;
+        return 1;
+    }
+
+    public void finishGood() {
+        finishWithResult(Activity.RESULT_OK, null);
+    }
+
+    public void finishBad(String error) {
+        finishWithResult(Activity.RESULT_CANCELED, (new Intent()).setAction(error));
+    }
+
+    public void finishWithResult(int resultCode, Intent data) {
+        RuntimeException where = new RuntimeException("Original error was here");
+        where.fillInStackTrace();
+        finishWithResult(resultCode, data, where);
+    }
+
+    public void finishWithResult(int resultCode, Intent data, RuntimeException where) {
+        synchronized (this) {
+            //System.out.println("*** Activity finished!!");
+            mResultCode = resultCode;
+            mData = data;
+            mResultStack = where;
+            mFinished = true;
+            notifyAll();
+        }
+    }
+
+    public int runLaunchpad(String action) {
+        LaunchpadActivity.setCallingTest(this);
+
+        synchronized (this) {
+            mIntent.setAction(action);
+            mFinished = false;
+            //System.out.println("*** Starting: " + mIntent);
+            mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            mContext.startActivity(mIntent);
+        }
+
+        return waitForResultOrThrow(60 * 1000);
+    }
+
+    public int waitForResultOrThrow(int timeoutMs) {
+        return waitForResultOrThrow(timeoutMs, null);
+    }
+
+    public int waitForResultOrThrow(int timeoutMs, String expected) {
+        int res = waitForResult(timeoutMs, expected);
+
+        if (res == Activity.RESULT_CANCELED) {
+            if (mResultStack != null) {
+                throw new RuntimeException(
+                        mData != null ? mData.toString() : "Unable to launch",
+                        mResultStack);
+            } else {
+                throw new RuntimeException(
+                        mData != null ? mData.toString() : "Unable to launch");
+            }
+        }
+        return res;
+    }
+
+    public int waitForResult(int timeoutMs, String expected) {
+        mExpecting = expected;
+
+        long endTime = System.currentTimeMillis() + timeoutMs;
+
+        boolean timeout = false;
+        synchronized (this) {
+            while (!mFinished) {
+                long delay = endTime - System.currentTimeMillis();
+                if (delay < 0) {
+                    timeout = true;
+                    break;
+                }
+
+                try {
+                    wait(delay);
+                } catch (java.lang.InterruptedException e) {
+                    // do nothing
+                }
+            }
+        }
+
+        mFinished = false;
+
+        if (timeout) {
+            mResultCode = Activity.RESULT_CANCELED;
+            onTimeout();
+        }
+        return mResultCode;
+    }
+
+    public int getResultCode() {
+        return mResultCode;
+    }
+    
+    public Intent getResultData() {
+        return mData;
+    }
+    
+    public RuntimeException getResultStack() {
+        return mResultStack;
+    }
+    
+    public void onTimeout() {
+        String msg = mExpecting == null
+                ? "Timeout" : ("Timeout while expecting " + mExpecting);
+        finishWithResult(Activity.RESULT_CANCELED, (new Intent()).setAction(msg));
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/BroadcastTest.java b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
new file mode 100644
index 0000000..4b1f9fd
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/BroadcastTest.java
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.test.FlakyTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+import java.util.Arrays;
+
+public class BroadcastTest extends ActivityTestsBase {
+    public static final int BROADCAST_TIMEOUT = 5 * 1000;
+
+    public static final String BROADCAST_REGISTERED =
+            "com.android.frameworks.coretests.activity.BROADCAST_REGISTERED";
+    public static final String BROADCAST_LOCAL =
+            "com.android.frameworks.coretests.activity.BROADCAST_LOCAL";
+    public static final String BROADCAST_LOCAL_GRANTED =
+            "com.android.frameworks.coretests.activity.BROADCAST_LOCAL_GRANTED";
+    public static final String BROADCAST_LOCAL_DENIED =
+            "com.android.frameworks.coretests.activity.BROADCAST_LOCAL_DENIED";
+    public static final String BROADCAST_REMOTE =
+            "com.android.frameworks.coretests.activity.BROADCAST_REMOTE";
+    public static final String BROADCAST_REMOTE_GRANTED =
+            "com.android.frameworks.coretests.activity.BROADCAST_REMOTE_GRANTED";
+    public static final String BROADCAST_REMOTE_DENIED =
+            "com.android.frameworks.coretests.activity.BROADCAST_REMOTE_DENIED";
+    public static final String BROADCAST_ALL =
+            "com.android.frameworks.coretests.activity.BROADCAST_ALL";
+    public static final String BROADCAST_MULTI =
+            "com.android.frameworks.coretests.activity.BROADCAST_MULTI";
+    public static final String BROADCAST_ABORT =
+            "com.android.frameworks.coretests.activity.BROADCAST_ABORT";
+
+    public static final String BROADCAST_STICKY1 =
+            "com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
+    public static final String BROADCAST_STICKY2 =
+            "com.android.frameworks.coretests.activity.BROADCAST_STICKY2";
+
+    public static final String BROADCAST_FAIL_REGISTER =
+            "com.android.frameworks.coretests.activity.BROADCAST_FAIL_REGISTER";
+    public static final String BROADCAST_FAIL_BIND =
+            "com.android.frameworks.coretests.activity.BROADCAST_FAIL_BIND";
+
+    public static final String RECEIVER_REG = "receiver-reg";
+    public static final String RECEIVER_LOCAL = "receiver-local";
+    public static final String RECEIVER_REMOTE = "receiver-remote";
+    public static final String RECEIVER_ABORT = "receiver-abort";
+    public static final String RECEIVER_RESULTS = "receiver-results";
+
+    public static final String DATA_1 = "one";
+    public static final String DATA_2 = "two";
+
+    public static final int GOT_RECEIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+    public static final int ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
+
+    private String[] mExpectedReceivers = null;
+    private int mNextReceiver;
+
+    private String[] mExpectedData = null;
+    private boolean[] mReceivedData = null;
+
+    boolean mReceiverRegistered = false;
+
+    public void setExpectedReceivers(String[] receivers) {
+        mExpectedReceivers = receivers;
+        mNextReceiver = 0;
+    }
+
+    public void setExpectedData(String[] data) {
+        mExpectedData = data;
+        mReceivedData = new boolean[data.length];
+    }
+
+    public void onTimeout() {
+        String msg = "Timeout";
+        if (mExpectedReceivers != null && mNextReceiver < mExpectedReceivers.length) {
+            msg = msg + " waiting for " + mExpectedReceivers[mNextReceiver];
+        }
+        finishBad(msg);
+    }
+
+    public Intent makeBroadcastIntent(String action) {
+        Intent intent = new Intent(action, null);
+        intent.putExtra("caller", mCallTarget);
+        return intent;
+    }
+
+    public void finishWithResult(int resultCode, Intent data) {
+        unregisterMyReceiver();
+        super.finishWithResult(resultCode, data);
+    }
+
+    public final void gotReceive(String name, Intent intent) {
+        synchronized (this) {
+
+            //System.out.println("Got receive: " + name);
+            //System.out.println(mNextReceiver + " in " + mExpectedReceivers);
+            //new RuntimeException("stack").printStackTrace();
+
+            addIntermediate(name);
+
+            if (mExpectedData != null) {
+                int n = mExpectedData.length;
+                int i;
+                boolean prev = false;
+                for (i = 0; i < n; i++) {
+                    if (mExpectedData[i].equals(intent.getStringExtra("test"))) {
+                        if (mReceivedData[i]) {
+                            prev = true;
+                            continue;
+                        }
+                        mReceivedData[i] = true;
+                        break;
+                    }
+                }
+                if (i >= n) {
+                    if (prev) {
+                        finishBad("Receive got data too many times: "
+                                + intent.getStringExtra("test"));
+                    } else {
+                        finishBad("Receive got unexpected data: "
+                                + intent.getStringExtra("test"));
+                    }
+                    new RuntimeException("stack").printStackTrace();
+                    return;
+                }
+            }
+
+            if (mNextReceiver >= mExpectedReceivers.length) {
+                finishBad("Got too many onReceiveIntent() calls!");
+//                System.out.println("Too many intents received: now at "
+//                        + mNextReceiver + ", expect list: "
+//                        + Arrays.toString(mExpectedReceivers));
+                fail("Got too many onReceiveIntent() calls!");
+            } else if (!mExpectedReceivers[mNextReceiver].equals(name)) {
+                finishBad("Receive out of order: got " + name
+                        + " but expected "
+                        + mExpectedReceivers[mNextReceiver]);
+                fail("Receive out of order: got " + name
+                        + " but expected "
+                        + mExpectedReceivers[mNextReceiver]);
+            } else {
+                mNextReceiver++;
+                if (mNextReceiver == mExpectedReceivers.length) {
+                    finishTest();
+                }
+            }
+        }
+    }
+
+    public void registerMyReceiver(IntentFilter filter, String permission) {
+        mReceiverRegistered = true;
+        //System.out.println("Registering: " + mReceiver);
+        getContext().registerReceiver(mReceiver, filter, permission, null);
+    }
+
+    public void unregisterMyReceiver() {
+        if (mReceiverRegistered) {
+            unregisterMyReceiverNoCheck();
+        }
+    }
+
+    public void unregisterMyReceiverNoCheck() {
+        mReceiverRegistered = false;
+        //System.out.println("Unregistering: " + mReceiver);
+        getContext().unregisterReceiver(mReceiver);
+    }
+
+    public void onRegisteredReceiver(Intent intent) {
+        gotReceive(RECEIVER_REG, intent);
+    }
+
+    private Binder mCallTarget = new Binder() {
+        public boolean onTransact(int code, Parcel data, Parcel reply,
+                int flags) {
+            data.setDataPosition(0);
+            data.enforceInterface(LaunchpadActivity.LAUNCH);
+            if (code == GOT_RECEIVE_TRANSACTION) {
+                String name = data.readString();
+                gotReceive(name, null);
+                return true;
+            } else if (code == ERROR_TRANSACTION) {
+                finishBad(data.readString());
+                return true;
+            }
+            return false;
+        }
+    };
+
+    private void finishTest() {
+        if (mReceiverRegistered) {
+            addIntermediate("before-unregister");
+            unregisterMyReceiver();
+        }
+        finishTiming(true);
+        finishGood();
+    }
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            //System.out.println("Receive in: " + this + ": " + intent);
+            onRegisteredReceiver(intent);
+        }
+    };
+
+    // Mark flaky until http://b/issue?id=1191607 is resolved.
+    @FlakyTest(tolerance=2)
+    public void testRegistered() throws Exception {
+        runLaunchpad(LaunchpadActivity.BROADCAST_REGISTERED);
+    }
+
+    public void testLocal() throws Exception {
+        runLaunchpad(LaunchpadActivity.BROADCAST_LOCAL);
+    }
+
+    public void testRemote() throws Exception {
+        runLaunchpad(LaunchpadActivity.BROADCAST_REMOTE);
+    }
+
+    public void testAbort() throws Exception {
+        runLaunchpad(LaunchpadActivity.BROADCAST_ABORT);
+    }
+
+    @FlakyTest(tolerance=2)
+    public void testAll() throws Exception {
+        runLaunchpad(LaunchpadActivity.BROADCAST_ALL);
+    }
+
+    @FlakyTest(tolerance=2)
+    public void testMulti() throws Exception {
+        runLaunchpad(LaunchpadActivity.BROADCAST_MULTI);
+    }
+
+    private class TestBroadcastReceiver extends BroadcastReceiver {
+        public boolean mHaveResult = false;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (BroadcastTest.this) {
+                mHaveResult = true;
+                BroadcastTest.this.notifyAll();
+            }
+        }
+    }
+
+    public void testResult() throws Exception {
+        TestBroadcastReceiver broadcastReceiver = new TestBroadcastReceiver();
+
+        synchronized (this) {
+            Bundle map = new Bundle();
+            map.putString("foo", "you");
+            map.putString("remove", "me");
+            getContext().sendOrderedBroadcast(
+                    new Intent("com.android.frameworks.coretests.activity.BROADCAST_RESULT"),
+                    null, broadcastReceiver, null, 1, "foo", map);
+            while (!broadcastReceiver.mHaveResult) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+
+            //System.out.println("Code: " + mResultCode + ", data: " + mResultData);
+            //System.out.println("Extras: " + mResultExtras);
+
+            assertEquals("Incorrect code: " + broadcastReceiver.getResultCode(),
+                    3, broadcastReceiver.getResultCode());
+
+            assertEquals("bar", broadcastReceiver.getResultData());
+
+            Bundle resultExtras = broadcastReceiver.getResultExtras(false);
+            assertEquals("them", resultExtras.getString("bar"));
+            assertEquals("you", resultExtras.getString("foo"));
+            assertNull(resultExtras.getString("remove"));
+        }
+    }
+
+    public void testSetSticky() throws Exception {
+        Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+        intent.putExtra("test", LaunchpadActivity.DATA_1);
+        ActivityManagerNative.getDefault().unbroadcastIntent(null, intent);
+
+        ActivityManagerNative.broadcastStickyIntent(intent, null);
+        addIntermediate("finished-broadcast");
+
+        IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
+        Intent sticky = getContext().registerReceiver(null, filter);
+        assertNotNull("Sticky not found", sticky);
+        assertEquals(LaunchpadActivity.DATA_1, sticky.getStringExtra("test"));
+    }
+
+    public void testClearSticky() throws Exception {
+        Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+        intent.putExtra("test", LaunchpadActivity.DATA_1);
+        ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+        ActivityManagerNative.getDefault().unbroadcastIntent(
+                null, new Intent(LaunchpadActivity.BROADCAST_STICKY1, null));
+        addIntermediate("finished-unbroadcast");
+
+        IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
+        Intent sticky = getContext().registerReceiver(null, filter);
+        assertNull("Sticky not found", sticky);
+    }
+
+    public void testReplaceSticky() throws Exception {
+        Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+        intent.putExtra("test", LaunchpadActivity.DATA_1);
+        ActivityManagerNative.broadcastStickyIntent(intent, null);
+        intent.putExtra("test", LaunchpadActivity.DATA_2);
+
+        ActivityManagerNative.broadcastStickyIntent(intent, null);
+        addIntermediate("finished-broadcast");
+
+        IntentFilter filter = new IntentFilter(LaunchpadActivity.BROADCAST_STICKY1);
+        Intent sticky = getContext().registerReceiver(null, filter);
+        assertNotNull("Sticky not found", sticky);
+        assertEquals(LaunchpadActivity.DATA_2, sticky.getStringExtra("test"));
+    }
+
+    // Marking flaky until http://b/issue?id=1191337 is resolved
+    @FlakyTest(tolerance=2)
+    public void testReceiveSticky() throws Exception {
+        Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+        intent.putExtra("test", LaunchpadActivity.DATA_1);
+        ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+        runLaunchpad(LaunchpadActivity.BROADCAST_STICKY1);
+    }
+
+    // Marking flaky until http://b/issue?id=1191337 is resolved
+    @FlakyTest(tolerance=2)
+    public void testReceive2Sticky() throws Exception {
+        Intent intent = new Intent(LaunchpadActivity.BROADCAST_STICKY1, null);
+        intent.putExtra("test", LaunchpadActivity.DATA_1);
+        ActivityManagerNative.broadcastStickyIntent(intent, null);
+        intent = new Intent(LaunchpadActivity.BROADCAST_STICKY2, null);
+        intent.putExtra("test", LaunchpadActivity.DATA_2);
+        ActivityManagerNative.broadcastStickyIntent(intent, null);
+
+        runLaunchpad(LaunchpadActivity.BROADCAST_STICKY2);
+    }
+
+    public void testRegisteredReceivePermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_REG});
+        registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_GRANTED);
+        addIntermediate("after-register");
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testRegisteredReceivePermissionDenied() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+        registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_DENIED);
+        addIntermediate("after-register");
+
+        BroadcastReceiver finish = new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        getContext().sendOrderedBroadcast(
+                makeBroadcastIntent(BROADCAST_REGISTERED),
+                null, finish, null, Activity.RESULT_CANCELED, null, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testRegisteredBroadcastPermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_REG});
+        registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), null);
+        addIntermediate("after-register");
+        getContext().sendBroadcast(
+                makeBroadcastIntent(BROADCAST_REGISTERED),
+                PERMISSION_GRANTED);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testRegisteredBroadcastPermissionDenied() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+        registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), null);
+        addIntermediate("after-register");
+
+        BroadcastReceiver finish = new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        getContext().sendOrderedBroadcast(
+                makeBroadcastIntent(BROADCAST_REGISTERED),
+                PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
+                null, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testLocalReceivePermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL_GRANTED));
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testLocalReceivePermissionDenied() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+        BroadcastReceiver finish = new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        getContext().sendOrderedBroadcast(
+                makeBroadcastIntent(BROADCAST_LOCAL_DENIED),
+                null, finish, null, Activity.RESULT_CANCELED,
+                null, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testLocalBroadcastPermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+        getContext().sendBroadcast(
+                makeBroadcastIntent(BROADCAST_LOCAL),
+                PERMISSION_GRANTED);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testLocalBroadcastPermissionDenied() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+        BroadcastReceiver finish = new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        getContext().sendOrderedBroadcast(
+                makeBroadcastIntent(BROADCAST_LOCAL),
+                PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
+                null, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testRemoteReceivePermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_REMOTE});
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE_GRANTED));
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testRemoteReceivePermissionDenied() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+        BroadcastReceiver finish = new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        getContext().sendOrderedBroadcast(
+                makeBroadcastIntent(BROADCAST_REMOTE_DENIED),
+                null, finish, null, Activity.RESULT_CANCELED,
+                null, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testRemoteBroadcastPermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_REMOTE});
+        getContext().sendBroadcast(
+                makeBroadcastIntent(BROADCAST_REMOTE),
+                PERMISSION_GRANTED);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testRemoteBroadcastPermissionDenied() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+        BroadcastReceiver finish = new BroadcastReceiver() {
+            public void onReceive(Context context, Intent intent) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        getContext().sendOrderedBroadcast(
+                makeBroadcastIntent(BROADCAST_REMOTE),
+                PERMISSION_DENIED, finish, null, Activity.RESULT_CANCELED,
+                null, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testReceiverCanNotRegister() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_REGISTER));
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testReceiverCanNotBind() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+        getContext().sendBroadcast(makeBroadcastIntent(BROADCAST_FAIL_BIND));
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+    }
+
+    public void testLocalUnregisterTwice() throws Exception {
+        registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), null);
+        unregisterMyReceiverNoCheck();
+        try {
+            unregisterMyReceiverNoCheck();
+            fail("No exception thrown on second unregister");
+        } catch (IllegalArgumentException e) {
+            Log.i("foo", "Unregister exception", e);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ClearTop.java b/core/tests/coretests/src/android/app/activity/ClearTop.java
new file mode 100644
index 0000000..a5ee2ce
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ClearTop.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ClearTop extends Activity {
+    public static final String WAIT_CLEAR_TASK = "waitClearTask";
+    
+    public ClearTop() {
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        //Log.i("foo", "Creating: " + this);
+        Intent intent = new Intent(getIntent()).setAction(LocalScreen.CLEAR_TASK)
+                .setClass(this, LocalScreen.class);
+        startActivity(intent);
+    }
+    
+    @Override
+    public void onNewIntent(Intent intent) {
+        //Log.i("foo", "New intent in " + this + ": " + intent);
+        if (LocalScreen.CLEAR_TASK.equals(intent.getAction())) {
+            setResult(RESULT_OK);
+        } else {
+            setResult(RESULT_CANCELED, new Intent().setAction(
+                    "New intent received " + intent + ", expecting action "
+                    + TestedScreen.CLEAR_TASK));
+        }
+        finish();
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/IntentSenderTest.java b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
new file mode 100644
index 0000000..3c30915
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/IntentSenderTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.test.suitebuilder.annotation.Suppress;
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.Suppress;
+
+public class IntentSenderTest extends BroadcastTest {
+
+    public void testRegisteredReceivePermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_REG});
+        registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_GRANTED);
+        addIntermediate("after-register");
+        PendingIntent is = PendingIntent.getBroadcast(getContext(), 0,
+                makeBroadcastIntent(BROADCAST_REGISTERED), 0);
+        is.send();
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+        is.cancel();
+    }
+
+    public void testRegisteredReceivePermissionDenied() throws Exception {
+        final Intent intent = makeBroadcastIntent(BROADCAST_REGISTERED);
+
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+        registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED), PERMISSION_DENIED);
+        addIntermediate("after-register");
+
+        PendingIntent.OnFinished finish = new PendingIntent.OnFinished() {
+            public void onSendFinished(PendingIntent pi, Intent intent,
+                    int resultCode, String resultData, Bundle resultExtras) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        PendingIntent is = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+        is.send(Activity.RESULT_CANCELED, finish, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+        is.cancel();
+    }
+
+    public void testLocalReceivePermissionGranted() throws Exception {
+        setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+        PendingIntent is = PendingIntent.getBroadcast(getContext(), 0,
+                makeBroadcastIntent(BROADCAST_LOCAL_GRANTED), 0);
+        is.send();
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+        is.cancel();
+    }
+
+    public void testLocalReceivePermissionDenied() throws Exception {
+        final Intent intent = makeBroadcastIntent(BROADCAST_LOCAL_DENIED);
+
+        setExpectedReceivers(new String[]{RECEIVER_RESULTS});
+
+        PendingIntent.OnFinished finish = new PendingIntent.OnFinished() {
+            public void onSendFinished(PendingIntent pi, Intent intent,
+                    int resultCode, String resultData, Bundle resultExtras) {
+                gotReceive(RECEIVER_RESULTS, intent);
+            }
+        };
+
+        PendingIntent is = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
+        is.send(Activity.RESULT_CANCELED, finish, null);
+        waitForResultOrThrow(BROADCAST_TIMEOUT);
+        is.cancel();
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/LaunchTest.java b/core/tests/coretests/src/android/app/activity/LaunchTest.java
new file mode 100644
index 0000000..5893fd0
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LaunchTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.ComponentName;
+import android.test.suitebuilder.annotation.LargeTest;
+
+public class LaunchTest extends ActivityTestsBase {
+
+    @LargeTest
+    public void testColdActivity() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), TestedActivity.class));
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    @LargeTest
+    public void testLocalActivity() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), LocalActivity.class));
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    @LargeTest
+    public void testColdScreen() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), TestedScreen.class));
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    @LargeTest
+    public void testLocalScreen() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), LocalScreen.class));
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    @LargeTest
+    public void testForwardResult() throws Exception {
+        runLaunchpad(LaunchpadActivity.FORWARD_RESULT);
+    }
+
+    // The following is disabled until we can catch and recover from
+    // application errors.
+    public void xxtestBadParcelable() throws Exception {
+        // All we really care about for this test is that the system
+        // doesn't crash.
+        runLaunchpad(LaunchpadActivity.BAD_PARCELABLE);
+    }
+
+    @LargeTest
+    public void testClearTopInCreate() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), ClearTop.class));
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    @LargeTest
+    public void testClearTopWhileResumed() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), ClearTop.class));
+        mIntent.putExtra(ClearTop.WAIT_CLEAR_TASK, true);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+}
+
+
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
new file mode 100644
index 0000000..7662456
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadActivity.java
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.test.PerformanceTestCase;
+import android.util.Log;
+
+class MyBadParcelable implements Parcelable {
+    public MyBadParcelable() {
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString("I am bad");
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<MyBadParcelable> CREATOR
+            = new Parcelable.Creator<MyBadParcelable>() {
+        public MyBadParcelable createFromParcel(Parcel in) {
+            return new MyBadParcelable(in);
+        }
+
+        public MyBadParcelable[] newArray(int size) {
+            return new MyBadParcelable[size];
+        }
+    };
+
+    public MyBadParcelable(Parcel in) {
+        String nm = in.readString();
+    }
+}
+
+public class LaunchpadActivity extends Activity {
+    public interface CallingTest extends PerformanceTestCase.Intermediates {
+        public void startTiming(boolean realTime);
+        public void addIntermediate(String name);
+        public void addIntermediate(String name, long timeInNS);
+        public void finishTiming(boolean realTime);
+        public void activityFinished(int resultCode, Intent data,
+                RuntimeException where);
+    }
+
+    // Also used as the Binder interface descriptor string in these tests
+    public static final String LAUNCH = "com.android.frameworks.coretests.activity.LAUNCH";
+
+    public static final String FORWARD_RESULT =
+            "com.android.frameworks.coretests.activity.FORWARD_RESULT";
+    public static final String RETURNED_RESULT =
+            "com.android.frameworks.coretests.activity.RETURNED_RESULT";
+
+    public static final String BAD_PARCELABLE =
+            "comcom.android.frameworks.coretests.activity.BAD_PARCELABLE";
+
+    public static final int LAUNCHED_RESULT = 1;
+    public static final int FORWARDED_RESULT = 2;
+
+    public static final String LIFECYCLE_BASIC =
+            "com.android.frameworks.coretests.activity.LIFECYCLE_BASIC";
+    public static final String LIFECYCLE_SCREEN =
+            "com.android.frameworks.coretests.activity.LIFECYCLE_SCREEN";
+    public static final String LIFECYCLE_DIALOG =
+            "com.android.frameworks.coretests.activity.LIFECYCLE_DIALOG";
+    public static final String LIFECYCLE_FINISH_CREATE =
+            "com.android.frameworks.coretests.activity.LIFECYCLE_FINISH_CREATE";
+    public static final String LIFECYCLE_FINISH_START =
+            "com.android.frameworks.coretests.activity.LIFECYCLE_FINISH_START";
+
+    public static final String BROADCAST_REGISTERED =
+            "com.android.frameworks.coretests.activity.BROADCAST_REGISTERED";
+    public static final String BROADCAST_LOCAL =
+            "com.android.frameworks.coretests.activity.BROADCAST_LOCAL";
+    public static final String BROADCAST_REMOTE =
+            "com.android.frameworks.coretests.activity.BROADCAST_REMOTE";
+    public static final String BROADCAST_ALL =
+            "com.android.frameworks.coretests.activity.BROADCAST_ALL";
+    public static final String BROADCAST_REPEAT =
+        "com.android.frameworks.coretests.activity.BROADCAST_REPEAT";
+    public static final String BROADCAST_MULTI =
+            "com.android.frameworks.coretests.activity.BROADCAST_MULTI";
+    public static final String BROADCAST_ABORT =
+            "com.android.frameworks.coretests.activity.BROADCAST_ABORT";
+
+    public static final String BROADCAST_STICKY1 =
+            "com.android.frameworks.coretests.activity.BROADCAST_STICKY1";
+    public static final String BROADCAST_STICKY2 =
+            "com.android.frameworks.coretests.activity.BROADCAST_STICKY2";
+
+    public static final String RECEIVER_REG = "receiver-reg";
+    public static final String RECEIVER_LOCAL = "receiver-local";
+    public static final String RECEIVER_REMOTE = "receiver-remote";
+    public static final String RECEIVER_ABORT = "receiver-abort";
+
+    public static final String DATA_1 = "one";
+    public static final String DATA_2 = "two";
+
+    public static final String ON_START = "onStart";
+    public static final String ON_RESTART = "onRestart";
+    public static final String ON_RESUME = "onResume";
+    public static final String ON_FREEZE = "onSaveInstanceState";
+    public static final String ON_PAUSE = "onPause";
+    public static final String ON_STOP = "onStop";
+    public static final String ON_DESTROY = "onDestroy";
+
+    public static final String DO_FINISH = "finish";
+    public static final String DO_LOCAL_SCREEN = "local-screen";
+    public static final String DO_LOCAL_DIALOG = "local-dialog";
+
+    private boolean mBadParcelable = false;
+
+    private boolean mStarted = false;
+    private long mStartTime;
+
+    private int mResultCode = RESULT_CANCELED;
+    private Intent mData = (new Intent()).setAction("No result received");
+    private RuntimeException mResultStack = null;
+
+    private String[] mExpectedLifecycle = null;
+    private int mNextLifecycle;
+
+    private String[] mExpectedReceivers = null;
+    private int mNextReceiver;
+
+    private String[] mExpectedData = null;
+    private boolean[] mReceivedData = null;
+
+    boolean mReceiverRegistered = false;
+
+    private static CallingTest sCallingTest = null;
+
+    public static void setCallingTest(CallingTest ct) {
+        sCallingTest = ct;
+    }
+
+    public LaunchpadActivity() {
+        mStartTime = System.currentTimeMillis();
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        String action = getIntent().getAction();
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "CREATE lauchpad "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        if (LIFECYCLE_BASIC.equals(action)) {
+            setExpectedLifecycle(new String[]{ON_START, ON_RESUME,
+                    DO_FINISH, ON_PAUSE, ON_STOP, ON_DESTROY});
+        } else if (LIFECYCLE_SCREEN.equals(action)) {
+            setExpectedLifecycle(new String[]{ON_START, ON_RESUME,
+                    DO_LOCAL_SCREEN, ON_FREEZE, ON_PAUSE, ON_STOP,
+                    ON_RESTART, ON_START, ON_RESUME,
+                    DO_FINISH, ON_PAUSE, ON_STOP, ON_DESTROY});
+        } else if (LIFECYCLE_DIALOG.equals(action)) {
+            setExpectedLifecycle(new String[]{ON_START, ON_RESUME,
+                    DO_LOCAL_DIALOG, ON_FREEZE, ON_PAUSE, ON_RESUME,
+                    DO_FINISH, ON_PAUSE, ON_STOP, ON_DESTROY});
+        } else if (LIFECYCLE_FINISH_CREATE.equals(action)) {
+            // This one behaves a little differently when running in a group.
+            if (getParent() == null) {
+                setExpectedLifecycle(new String[]{ON_DESTROY});
+            } else {
+                setExpectedLifecycle(new String[]{ON_START, ON_STOP, ON_DESTROY});
+            }
+            finish();
+        } else if (LIFECYCLE_FINISH_START.equals(action)) {
+            setExpectedLifecycle(new String[]{ON_START, DO_FINISH,
+                    ON_STOP, ON_DESTROY});
+        }
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "START lauchpad "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        checkLifecycle(ON_START);
+    }
+
+    @Override
+    protected void onRestart() {
+        super.onStart();
+        checkLifecycle(ON_RESTART);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "RESUME lauchpad "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        checkLifecycle(ON_RESUME);
+
+        if (!mStarted) {
+            mStarted = true;
+
+            mHandler.postDelayed(mTimeout, 5 * 1000);
+
+            String action = getIntent().getAction();
+
+            sCallingTest.startTiming(true);
+
+            if (LAUNCH.equals(action)) {
+                Intent intent = getIntent();
+                intent.setFlags(0);
+                intent.setComponent((ComponentName)
+                        intent.getParcelableExtra("component"));
+                //System.out.println("*** Launchpad is starting: comp=" + intent.component);
+                startActivityForResult(intent, LAUNCHED_RESULT);
+            } else if (FORWARD_RESULT.equals(action)) {
+                Intent intent = getIntent();
+                intent.setFlags(0);
+                intent.setClass(this, LocalScreen.class);
+                startActivityForResult(intent, FORWARDED_RESULT);
+            } else if (BAD_PARCELABLE.equals(action)) {
+                mBadParcelable = true;
+                Intent intent = getIntent();
+                intent.setFlags(0);
+                intent.setClass(this, LocalScreen.class);
+                startActivityForResult(intent, LAUNCHED_RESULT);
+            } else if (BROADCAST_REGISTERED.equals(action)) {
+                setExpectedReceivers(new String[]{RECEIVER_REG});
+                registerMyReceiver(new IntentFilter(BROADCAST_REGISTERED));
+                sCallingTest.addIntermediate("after-register");
+                sendBroadcast(makeBroadcastIntent(BROADCAST_REGISTERED));
+            } else if (BROADCAST_LOCAL.equals(action)) {
+                setExpectedReceivers(new String[]{RECEIVER_LOCAL});
+                sendBroadcast(makeBroadcastIntent(BROADCAST_LOCAL));
+            } else if (BROADCAST_REMOTE.equals(action)) {
+                setExpectedReceivers(new String[]{RECEIVER_REMOTE});
+                sendBroadcast(makeBroadcastIntent(BROADCAST_REMOTE));
+            } else if (BROADCAST_ALL.equals(action)) {
+                setExpectedReceivers(new String[]{
+                        RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL});
+                registerMyReceiver(new IntentFilter(BROADCAST_ALL));
+                sCallingTest.addIntermediate("after-register");
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+            } else if (BROADCAST_MULTI.equals(action)) {
+                setExpectedReceivers(new String[]{
+                        RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+                        RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+                        RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+                        RECEIVER_LOCAL, RECEIVER_REMOTE,
+                        RECEIVER_LOCAL, RECEIVER_REMOTE,
+                        RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+                        RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+                        RECEIVER_REMOTE, RECEIVER_REG, RECEIVER_LOCAL,
+                        RECEIVER_REMOTE, RECEIVER_LOCAL,
+                        RECEIVER_REMOTE, RECEIVER_LOCAL});
+                registerMyReceiver(new IntentFilter(BROADCAST_ALL));
+                sCallingTest.addIntermediate("after-register");
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_LOCAL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REMOTE), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ALL), null);
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_REPEAT), null);
+            } else if (BROADCAST_ABORT.equals(action)) {
+                setExpectedReceivers(new String[]{
+                        RECEIVER_REMOTE, RECEIVER_ABORT});
+                registerMyReceiver(new IntentFilter(BROADCAST_ABORT));
+                sCallingTest.addIntermediate("after-register");
+                sendOrderedBroadcast(makeBroadcastIntent(BROADCAST_ABORT), null);
+            } else if (BROADCAST_STICKY1.equals(action)) {
+                setExpectedReceivers(new String[]{RECEIVER_REG});
+                setExpectedData(new String[]{DATA_1});
+                registerMyReceiver(new IntentFilter(BROADCAST_STICKY1));
+                sCallingTest.addIntermediate("after-register");
+            } else if (BROADCAST_STICKY2.equals(action)) {
+                setExpectedReceivers(new String[]{RECEIVER_REG, RECEIVER_REG});
+                setExpectedData(new String[]{DATA_1, DATA_2});
+                IntentFilter filter = new IntentFilter(BROADCAST_STICKY1);
+                filter.addAction(BROADCAST_STICKY2);
+                registerMyReceiver(filter);
+                sCallingTest.addIntermediate("after-register");
+            }
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle icicle) {
+        super.onSaveInstanceState(icicle);
+        checkLifecycle(ON_FREEZE);
+        if (mBadParcelable) {
+            icicle.putParcelable("baddy", new MyBadParcelable());
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "PAUSE lauchpad "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        checkLifecycle(ON_PAUSE);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "STOP lauchpad "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        checkLifecycle(ON_STOP);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode,
+            Intent data) {
+        switch (requestCode) {
+            case LAUNCHED_RESULT:
+                sCallingTest.finishTiming(true);
+                finishWithResult(resultCode, data);
+                break;
+            case FORWARDED_RESULT:
+                sCallingTest.finishTiming(true);
+                if (RETURNED_RESULT.equals(data.getAction())) {
+                    finishWithResult(resultCode, data);
+                } else {
+                    finishWithResult(RESULT_CANCELED, (new Intent()).setAction(
+                            "Bad data returned: " + data));
+                }
+                break;
+            default:
+                sCallingTest.finishTiming(true);
+                finishWithResult(RESULT_CANCELED, (new Intent()).setAction(
+                        "Unexpected request code: " + requestCode));
+                break;
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "DESTROY lauchpad "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        checkLifecycle(ON_DESTROY);
+        sCallingTest.activityFinished(mResultCode, mData, mResultStack);
+    }
+
+    private void setExpectedLifecycle(String[] lifecycle) {
+        mExpectedLifecycle = lifecycle;
+        mNextLifecycle = 0;
+    }
+
+    private void checkLifecycle(String where) {
+        if (mExpectedLifecycle == null) return;
+
+        if (mNextLifecycle >= mExpectedLifecycle.length) {
+            finishBad("Activity lifecycle incorrect: received " + where
+                    + " but don't expect any more calls");
+            mExpectedLifecycle = null;
+            return;
+        }
+        if (!mExpectedLifecycle[mNextLifecycle].equals(where)) {
+            finishBad("Activity lifecycle incorrect: received " + where
+                    + " but expected " + mExpectedLifecycle[mNextLifecycle]
+                    + " at " + mNextLifecycle);
+            mExpectedLifecycle = null;
+            return;
+        }
+
+        mNextLifecycle++;
+
+        if (mNextLifecycle >= mExpectedLifecycle.length) {
+            setTestResult(RESULT_OK, null);
+            return;
+        }
+
+        String next = mExpectedLifecycle[mNextLifecycle];
+        if (where.equals(ON_DESTROY)) {
+            finishBad("Activity lifecycle incorrect: received " + where
+                    + " but expected more actions (next is " + next + ")");
+            mExpectedLifecycle = null;
+            return;
+        } else if (next.equals(DO_FINISH)) {
+            mNextLifecycle++;
+            if (mNextLifecycle >= mExpectedLifecycle.length) {
+                setTestResult(RESULT_OK, null);
+            }
+            if (!isFinishing()) {
+                finish();
+            }
+        } else if (next.equals(DO_LOCAL_SCREEN)) {
+            mNextLifecycle++;
+            Intent intent = new Intent(TestedScreen.WAIT_BEFORE_FINISH);
+            intent.setClass(this, LocalScreen.class);
+            startActivity(intent);
+        } else if (next.equals(DO_LOCAL_DIALOG)) {
+            mNextLifecycle++;
+            Intent intent = new Intent(TestedScreen.WAIT_BEFORE_FINISH);
+            intent.setClass(this, LocalDialog.class);
+            startActivity(intent);
+        }
+    }
+
+    private void setExpectedReceivers(String[] receivers) {
+        mExpectedReceivers = receivers;
+        mNextReceiver = 0;
+    }
+
+    private void setExpectedData(String[] data) {
+        mExpectedData = data;
+        mReceivedData = new boolean[data.length];
+    }
+
+    private Intent makeBroadcastIntent(String action) {
+        Intent intent = new Intent(action, null);
+        intent.putExtra("caller", mCallTarget);
+        return intent;
+    }
+
+    private void finishGood() {
+        finishWithResult(RESULT_OK, null);
+    }
+
+    private void finishBad(String error) {
+        finishWithResult(RESULT_CANCELED, (new Intent()).setAction(error));
+    }
+
+    private void finishWithResult(int resultCode, Intent data) {
+        setTestResult(resultCode, data);
+        finish();
+    }
+
+    private void setTestResult(int resultCode, Intent data) {
+        mHandler.removeCallbacks(mTimeout);
+        unregisterMyReceiver();
+        mResultCode = resultCode;
+        mData = data;
+        mResultStack = new RuntimeException("Original error was here");
+        mResultStack.fillInStackTrace();
+    }
+
+    private void registerMyReceiver(IntentFilter filter) {
+        mReceiverRegistered = true;
+        //System.out.println("Registering: " + mReceiver);
+        registerReceiver(mReceiver, filter);
+    }
+
+    private void unregisterMyReceiver() {
+        if (mReceiverRegistered) {
+            mReceiverRegistered = false;
+            //System.out.println("Unregistering: " + mReceiver);
+            unregisterReceiver(mReceiver);
+        }
+    }
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+        }
+    };
+
+    static final int GOT_RECEIVE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
+    static final int ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
+
+    private Binder mCallTarget = new Binder() {
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
+            data.setDataPosition(0);
+            data.enforceInterface(LaunchpadActivity.LAUNCH);
+            if (code == GOT_RECEIVE_TRANSACTION) {
+                String name = data.readString();
+                gotReceive(name, null);
+                return true;
+            } else if (code == ERROR_TRANSACTION) {
+                finishBad(data.readString());
+                return true;
+            }
+            return false;
+        }
+    };
+
+    private final void gotReceive(String name, Intent intent) {
+        synchronized (this) {
+
+            //System.out.println("Got receive: " + name);
+            //System.out.println(mNextReceiver + " in " + mExpectedReceivers);
+            //new RuntimeException("stack").printStackTrace();
+
+            sCallingTest.addIntermediate(mNextReceiver + "-" + name);
+
+            if (mExpectedData != null) {
+                int n = mExpectedData.length;
+                int i;
+                boolean prev = false;
+                for (i = 0; i < n; i++) {
+                    if (mExpectedData[i].equals(intent.getStringExtra("test"))) {
+                        if (mReceivedData[i]) {
+                            prev = true;
+                            continue;
+                        }
+                        mReceivedData[i] = true;
+                        break;
+                    }
+                }
+                if (i >= n) {
+                    if (prev) {
+                        finishBad("Receive got data too many times: "
+                                + intent.getStringExtra("test"));
+                    } else {
+                        finishBad("Receive got unexpected data: "
+                                + intent.getStringExtra("test"));
+                    }
+                    return;
+                }
+            }
+
+            if (mNextReceiver >= mExpectedReceivers.length) {
+                finishBad("Got too many onReceiveIntent() calls!");
+//                System.out.println("Too many intents received: now at "
+//                                   + mNextReceiver + ", expect list: "
+//                                   + Arrays.toString(mExpectedReceivers));
+            } else if (!mExpectedReceivers[mNextReceiver].equals(name)) {
+                finishBad("Receive out of order: got " + name + " but expected "
+                        + mExpectedReceivers[mNextReceiver] + " at "
+                        + mNextReceiver);
+            } else {
+                mNextReceiver++;
+                if (mNextReceiver == mExpectedReceivers.length) {
+                    mHandler.post(mUnregister);
+                }
+            }
+
+        }
+    }
+
+    private Runnable mUnregister = new Runnable() {
+        public void run() {
+            if (mReceiverRegistered) {
+                sCallingTest.addIntermediate("before-unregister");
+                unregisterMyReceiver();
+            }
+            sCallingTest.finishTiming(true);
+            finishGood();
+        }
+    };
+
+    private Runnable mTimeout = new Runnable() {
+        public void run() {
+            Log.i("foo", "**** TIMEOUT");
+            String msg = "Timeout";
+            if (mExpectedReceivers != null
+                    && mNextReceiver < mExpectedReceivers.length) {
+                msg = msg + " waiting for " + mExpectedReceivers[mNextReceiver];
+            }
+            finishBad(msg);
+        }
+    };
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            //System.out.println("Receive in: " + this + ": " + intent);
+            gotReceive(RECEIVER_REG, intent);
+        }
+    };
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LaunchpadTabActivity.java b/core/tests/coretests/src/android/app/activity/LaunchpadTabActivity.java
new file mode 100644
index 0000000..79b860f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LaunchpadTabActivity.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.TabActivity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.TabHost;
+
+public class LaunchpadTabActivity extends TabActivity {
+    public LaunchpadTabActivity() {
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        
+        Intent tabIntent = new Intent(getIntent());
+        tabIntent.setComponent((ComponentName)tabIntent.getParcelableExtra("tab"));
+        
+        TabHost th = getTabHost();
+        TabHost.TabSpec ts = th.newTabSpec("1");
+        ts.setIndicator("One");
+        ts.setContent(tabIntent);
+        th.addTab(ts);
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LifecycleTest.java b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
new file mode 100644
index 0000000..768a9a4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LifecycleTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.test.FlakyTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+public class LifecycleTest extends ActivityTestsBase {
+    private Intent mTopIntent;
+    private Intent mTabIntent;
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTopIntent = mIntent;
+        mTabIntent = new Intent(mContext, LaunchpadTabActivity.class);
+        mTabIntent.putExtra("tab", new ComponentName(mContext,
+                LaunchpadActivity.class));
+    }
+
+    @LargeTest
+    public void testBasic() throws Exception {
+        mIntent = mTopIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_BASIC);
+    }
+
+    //Suppressing until 1285425 is fixed.
+    @Suppress
+    public void testTabBasic() throws Exception {
+        mIntent = mTabIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_BASIC);
+    }
+
+    //Marking flaky until bug 1164344 is fixed.
+    // @FlakyTest(tolerance=2)
+    // @LargeTest
+    public void testScreen() throws Exception {
+        mIntent = mTopIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_SCREEN);
+    }
+
+    //Marking flaky until bug 1164344 is fixed.
+    //@FlakyTest(tolerance=2)
+    //Suppressing until 1285425 is fixed.
+    @Suppress
+    public void testTabScreen() throws Exception {
+        mIntent = mTabIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_SCREEN);
+    }
+
+    //flaky test, removing from large suite until 1866891 is fixed
+    //@LargeTest
+    public void testDialog() throws Exception {
+        mIntent = mTopIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_DIALOG);
+    }
+
+    //Suppressing until 1285425 is fixed.
+    @Suppress
+    public void testTabDialog() throws Exception {
+        mIntent = mTabIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_DIALOG);
+    }
+
+    @MediumTest
+    public void testFinishCreate() throws Exception {
+        mIntent = mTopIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_CREATE);
+    }
+
+    //Suppressing until 1285425 is fixed.
+    @Suppress
+    public void testTabFinishCreate() throws Exception {
+        mIntent = mTabIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_CREATE);
+    }
+
+    @MediumTest
+    public void testFinishStart() throws Exception {
+        mIntent = mTopIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_START);
+    }
+    
+    //Suppressing until 1285425 is fixed.
+    @Suppress
+    public void testTabFinishStart() throws Exception {
+        mIntent = mTabIntent;
+        runLaunchpad(LaunchpadActivity.LIFECYCLE_FINISH_START);
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/LocalActivity.java b/core/tests/coretests/src/android/app/activity/LocalActivity.java
new file mode 100644
index 0000000..01f1fb6
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import java.util.Map;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+
+public class LocalActivity extends TestedActivity
+{
+    public LocalActivity()
+    {
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
new file mode 100644
index 0000000..2120a1d
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+class LocalDeniedReceiver extends BroadcastReceiver {
+    public LocalDeniedReceiver() {
+    }
+
+    public void onReceive(Context context, Intent intent) {
+        try {
+            IBinder caller = intent.getIBinderExtra("caller");
+            Parcel data = Parcel.obtain();
+            data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+            data.writeString(BroadcastTest.RECEIVER_LOCAL);
+            caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+            data.recycle();
+        } catch (RemoteException ex) {
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalDeniedService.java b/core/tests/coretests/src/android/app/activity/LocalDeniedService.java
new file mode 100644
index 0000000..3bdac22
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalDeniedService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+public class LocalDeniedService extends LocalService
+{
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalDialog.java b/core/tests/coretests/src/android/app/activity/LocalDialog.java
new file mode 100644
index 0000000..c92fa43
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalDialog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import java.util.Map;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+
+public class LocalDialog extends TestedScreen
+{
+    public LocalDialog()
+    {
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalGrantedReceiver.java b/core/tests/coretests/src/android/app/activity/LocalGrantedReceiver.java
new file mode 100644
index 0000000..c9e6ab4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalGrantedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class LocalGrantedReceiver extends BroadcastReceiver {
+    public LocalGrantedReceiver() {
+    }
+
+    public void onReceive(Context context, Intent intent) {
+        try {
+            IBinder caller = intent.getIBinderExtra("caller");
+            Parcel data = Parcel.obtain();
+            data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+            data.writeString(BroadcastTest.RECEIVER_LOCAL);
+            caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+            data.recycle();
+        } catch (RemoteException ex) {
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalGrantedService.java b/core/tests/coretests/src/android/app/activity/LocalGrantedService.java
new file mode 100644
index 0000000..7ab0fb4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalGrantedService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+public class LocalGrantedService extends LocalService
+{
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalProvider.java b/core/tests/coretests/src/android/app/activity/LocalProvider.java
new file mode 100644
index 0000000..085e622
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalProvider.java
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+package android.app.activity;
+
+import android.content.UriMatcher;
+import android.content.*;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.util.Config;
+import android.util.Log;
+
+/** Simple test provider that runs in the local process. */
+public class LocalProvider extends ContentProvider {
+    private static final String TAG = "LocalProvider";
+
+    private SQLiteOpenHelper mOpenHelper;
+
+    private static final int DATA = 1;
+    private static final int DATA_ID = 2;
+    private static final UriMatcher sURLMatcher = new UriMatcher(
+            UriMatcher.NO_MATCH);
+
+    static {
+        sURLMatcher.addURI("*", "data", DATA);
+        sURLMatcher.addURI("*", "data/#", DATA_ID);
+    }
+
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+        private static final String DATABASE_NAME = "local.db";
+        private static final int DATABASE_VERSION = 1;
+
+        public DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE data (" +
+                       "_id INTEGER PRIMARY KEY," +
+                       "text TEXT, " +
+                       "integer INTEGER);");
+
+            // insert alarms
+            db.execSQL("INSERT INTO data (text, integer) VALUES ('first data', 100);");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+            Log.w(TAG, "Upgrading test database from version " +
+                  oldVersion + " to " + currentVersion +
+                  ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS data");
+            onCreate(db);
+        }
+    }
+
+
+    public LocalProvider() {
+    }
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri url, String[] projectionIn, String selection,
+            String[] selectionArgs, String sort) {
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+
+        // Generate the body of the query
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case DATA:
+                qb.setTables("data");
+                break;
+            case DATA_ID:
+                qb.setTables("data");
+                qb.appendWhere("_id=");
+                qb.appendWhere(url.getPathSegments().get(1));
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown URL " + url);
+        }
+
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor ret = qb.query(db, projectionIn, selection, selectionArgs,
+                              null, null, sort);
+
+        if (ret == null) {
+            if (Config.LOGD) Log.d(TAG, "Alarms.query: failed");
+        } else {
+            ret.setNotificationUri(getContext().getContentResolver(), url);
+        }
+
+        return ret;
+    }
+
+    @Override
+    public String getType(Uri url) {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case DATA:
+                return "vnd.android.cursor.dir/vnd.google.unit_tests.local";
+            case DATA_ID:
+                return "vnd.android.cursor.item/vnd.google.unit_tests.local";
+            default:
+                throw new IllegalArgumentException("Unknown URL");
+        }
+    }
+
+    @Override
+    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+        int count;
+        long rowId = 0;
+        int match = sURLMatcher.match(url);
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        switch (match) {
+            case DATA_ID: {
+                String segment = url.getPathSegments().get(1);
+                rowId = Long.parseLong(segment);
+                count = db.update("data", values, "_id=" + rowId, null);
+                break;
+            }
+            default: {
+                throw new UnsupportedOperationException(
+                        "Cannot update URL: " + url);
+            }
+        }
+        if (Config.LOGD) Log.d(TAG, "*** notifyChange() rowId: " + rowId);
+        getContext().getContentResolver().notifyChange(url, null);
+        return count;
+    }
+
+
+    @Override
+    public Uri insert(Uri url, ContentValues initialValues) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri url, String where, String[] whereArgs) {
+        throw new UnsupportedOperationException("delete not supported");
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/LocalReceiver.java b/core/tests/coretests/src/android/app/activity/LocalReceiver.java
new file mode 100644
index 0000000..bfd543f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalReceiver.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ReceiverCallNotAllowedException;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class LocalReceiver extends BroadcastReceiver {
+    public LocalReceiver() {
+    }
+
+    public void onReceive(Context context, Intent intent) {
+        String resultString = LaunchpadActivity.RECEIVER_LOCAL;
+        if (BroadcastTest.BROADCAST_FAIL_REGISTER.equals(intent.getAction())) {
+            resultString = "Successfully registered, but expected it to fail";
+            try {
+                context.registerReceiver(this, new IntentFilter("foo.bar"));
+                context.unregisterReceiver(this);
+            } catch (ReceiverCallNotAllowedException e) {
+                //resultString = "This is the correct behavior but not yet implemented";
+                resultString = LaunchpadActivity.RECEIVER_LOCAL;
+            }
+        } else if (BroadcastTest.BROADCAST_FAIL_BIND.equals(intent.getAction())) {
+            resultString = "Successfully bound to service, but expected it to fail";
+            try {
+                ServiceConnection sc = new ServiceConnection() {
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                    }
+
+                    public void onServiceDisconnected(ComponentName name) {
+                    }
+                };
+                context.bindService(new Intent(context, LocalService.class), sc, 0);
+                context.unbindService(sc);
+            } catch (ReceiverCallNotAllowedException e) {
+                //resultString = "This is the correct behavior but not yet implemented";
+                resultString = LaunchpadActivity.RECEIVER_LOCAL;
+            }
+        } else if (LaunchpadActivity.BROADCAST_REPEAT.equals(intent.getAction())) {
+            Intent newIntent = new Intent(intent);
+            newIntent.setAction(LaunchpadActivity.BROADCAST_LOCAL);
+            context.sendOrderedBroadcast(newIntent, null);
+        }
+        try {
+            IBinder caller = intent.getIBinderExtra("caller");
+            Parcel data = Parcel.obtain();
+            data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+            data.writeString(resultString);
+            caller.transact(LaunchpadActivity.GOT_RECEIVE_TRANSACTION, data, null, 0);
+            data.recycle();
+        } catch (RemoteException ex) {
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalScreen.java b/core/tests/coretests/src/android/app/activity/LocalScreen.java
new file mode 100644
index 0000000..f7c8c33
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalScreen.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import java.util.Map;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+
+public class LocalScreen extends TestedScreen
+{
+    public LocalScreen()
+    {
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/LocalService.java b/core/tests/coretests/src/android/app/activity/LocalService.java
new file mode 100644
index 0000000..c31ca4b
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/LocalService.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.util.Log;
+
+public class LocalService extends Service {
+    private final IBinder mBinder = new Binder() {
+
+        @Override
+        protected boolean onTransact(int code, Parcel data, Parcel reply,
+                int flags) throws RemoteException {
+            if (code == ServiceTest.SET_REPORTER_CODE) {
+                data.enforceInterface(ServiceTest.SERVICE_LOCAL);
+                mReportObject = data.readStrongBinder();
+                return true;
+            } else {
+                return super.onTransact(code, data, reply, flags);
+            }
+        }
+        
+    };
+
+    private IBinder mReportObject;
+    private int mStartCount = 1;
+
+    public LocalService() {
+    }
+
+    @Override
+    public void onStart(Intent intent, int startId) {
+        //Log.i("LocalService", "onStart: " + intent);
+        if (intent.getExtras() != null) {
+            mReportObject = intent.getExtras().getIBinder(ServiceTest.REPORT_OBJ_NAME);
+            if (mReportObject != null) {
+                try {
+                    Parcel data = Parcel.obtain();
+                    data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+                    data.writeInt(mStartCount);
+                    mStartCount++;
+                    mReportObject.transact(
+                            ServiceTest.STARTED_CODE, data, null, 0);
+                    data.recycle();
+                } catch (RemoteException e) {
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.i("LocalService", "onDestroy: mReportObject=" + mReportObject);
+        if (mReportObject != null) {
+            try {
+                Parcel data = Parcel.obtain();
+                data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+                mReportObject.transact(
+                        ServiceTest.DESTROYED_CODE, data, null, 0);
+                data.recycle();
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.i("LocalService", "onBind: " + intent);
+        return mBinder;
+    }
+    
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Log.i("LocalService", "onUnbind: " + intent);
+        if (mReportObject != null) {
+            try {
+                Parcel data = Parcel.obtain();
+                data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+                mReportObject.transact(
+                        ServiceTest.UNBIND_CODE, data, null, 0);
+                data.recycle();
+            } catch (RemoteException e) {
+            }
+        }
+        return true;
+    }
+    
+    @Override
+    public void onRebind(Intent intent) {
+        Log.i("LocalService", "onUnbind: " + intent);
+        if (mReportObject != null) {
+            try {
+                Parcel data = Parcel.obtain();
+                data.writeInterfaceToken(ServiceTest.SERVICE_LOCAL);
+                mReportObject.transact(
+                        ServiceTest.REBIND_CODE, data, null, 0);
+                data.recycle();
+            } catch (RemoteException e) {
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/MetaDataTest.java b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
new file mode 100644
index 0000000..214bc91
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/MetaDataTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Bundle;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.frameworks.coretests.R;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Tests for meta-data associated with application components.
+ */
+public class MetaDataTest extends AndroidTestCase {
+
+    private void checkMetaData(ComponentName cn, PackageItemInfo ci)
+            throws IOException, XmlPullParserException {
+        assertNotNull("Unable to find component " + cn, ci);
+
+        Bundle md = ci.metaData;
+        assertNotNull("No meta data found", md);
+
+        assertEquals("foo", md.getString("com.android.frameworks.coretests.string"));
+        assertTrue(md.getBoolean("com.android.frameworks.coretests.boolean"));
+        assertEquals(100, md.getInt("com.android.frameworks.coretests.integer"));
+        assertEquals(0xff000000, md.getInt("com.android.frameworks.coretests.color"));
+
+        assertEquals((double) 1001,
+                Math.floor(md.getFloat("com.android.frameworks.coretests.float") * 10 + .5));
+
+        assertEquals(R.xml.metadata, md.getInt("com.android.frameworks.coretests.reference"));
+
+        XmlResourceParser xml = ci.loadXmlMetaData(mContext.getPackageManager(),
+                "com.android.frameworks.coretests.reference");
+        assertNotNull(xml);
+
+        int type;
+        while ((type = xml.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+        }
+        assertEquals(XmlPullParser.START_TAG, type);
+        assertEquals("thedata", xml.getName());
+
+        // method 1: direct access
+        final String rawAttr = xml.getAttributeValue(null, "rawText");
+        assertEquals("some raw text", rawAttr);
+
+        // method 2: direct access of typed value
+        final int rawColorIntAttr = xml.getAttributeIntValue(null, "rawColor", 0);
+        assertEquals(0xffffff00, rawColorIntAttr);
+        final String rawColorStrAttr = xml.getAttributeValue(null, "rawColor");
+        assertEquals("#ffffff00", rawColorStrAttr);
+
+        // method 2: direct access of resource attribute
+        final String nameSpace = "http://schemas.android.com/apk/res/android";
+        final int colorIntAttr = xml.getAttributeIntValue(nameSpace, "color", 0);
+        assertEquals(0xffff0000, colorIntAttr);
+        final String colorStrAttr = xml.getAttributeValue(nameSpace, "color");
+        assertEquals("#ffff0000", colorStrAttr);
+
+        // method 3: styled access (borrowing an attr from view system here)
+        TypedArray a = mContext.obtainStyledAttributes(xml,
+                android.R.styleable.TextView);
+        String styledAttr = a.getString(android.R.styleable.TextView_text);
+        assertEquals("text", styledAttr);
+        a.recycle();
+        
+        xml.close();
+    }
+
+    @SmallTest
+    public void testActivityWithData() throws Exception {
+        ComponentName cn = new ComponentName(mContext, LocalActivity.class);
+        ActivityInfo ai = mContext.getPackageManager().getActivityInfo(
+                cn, PackageManager.GET_META_DATA);
+
+        checkMetaData(cn, ai);
+
+        ai = mContext.getPackageManager().getActivityInfo(cn, 0);
+
+        assertNull("Meta data returned when not requested", ai.metaData);
+    }
+
+    @SmallTest
+    public void testReceiverWithData() throws Exception {
+        ComponentName cn = new ComponentName(mContext, LocalReceiver.class);
+        ActivityInfo ai = mContext.getPackageManager().getReceiverInfo(
+                cn, PackageManager.GET_META_DATA);
+
+        checkMetaData(cn, ai);
+
+        ai = mContext.getPackageManager().getReceiverInfo(cn, 0);
+
+        assertNull("Meta data returned when not requested", ai.metaData);
+    }
+
+    @SmallTest
+    public void testServiceWithData() throws Exception {
+        ComponentName cn = new ComponentName(mContext, LocalService.class);
+        ServiceInfo si = mContext.getPackageManager().getServiceInfo(
+                cn, PackageManager.GET_META_DATA);
+
+        checkMetaData(cn, si);
+
+        si = mContext.getPackageManager().getServiceInfo(cn, 0);
+
+        assertNull("Meta data returned when not requested", si.metaData);
+    }
+
+    @MediumTest
+    public void testProviderWithData() throws Exception {
+        ComponentName cn = new ComponentName(mContext, LocalProvider.class);
+        ProviderInfo pi = mContext.getPackageManager().resolveContentProvider(
+                "com.android.frameworks.coretests.LocalProvider",
+                PackageManager.GET_META_DATA);
+        checkMetaData(cn, pi);
+
+        pi = mContext.getPackageManager().resolveContentProvider(
+                "com.android.frameworks.coretests.LocalProvider", 0);
+
+        assertNull("Meta data returned when not requested", pi.metaData);
+    }
+
+    @SmallTest
+    public void testPermissionWithData() throws Exception {
+        ComponentName cn = new ComponentName("foo",
+                "com.android.frameworks.coretests.permission.TEST_GRANTED");
+        PermissionInfo pi = mContext.getPackageManager().getPermissionInfo(
+                cn.getClassName(), PackageManager.GET_META_DATA);
+        checkMetaData(cn, pi);
+
+        pi = mContext.getPackageManager().getPermissionInfo(
+                cn.getClassName(), 0);
+
+        assertNull("Meta data returned when not requested", pi.metaData);
+    }
+}
+
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
new file mode 100644
index 0000000..7c89346
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteDeniedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+class RemoteDeniedReceiver extends BroadcastReceiver {
+    public RemoteDeniedReceiver() {
+    }
+
+    public void onReceive(Context context, Intent intent) {
+        try {
+            IBinder caller = intent.getIBinderExtra("caller");
+            Parcel data = Parcel.obtain();
+            data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+            data.writeString(BroadcastTest.RECEIVER_REMOTE);
+            caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+            data.recycle();
+        } catch (RemoteException ex) {
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteGrantedReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteGrantedReceiver.java
new file mode 100644
index 0000000..0eca8f7
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteGrantedReceiver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class RemoteGrantedReceiver extends BroadcastReceiver {
+    public RemoteGrantedReceiver() {
+    }
+
+    public void onReceive(Context context, Intent intent) {
+        try {
+            IBinder caller = intent.getIBinderExtra("caller");
+            Parcel data = Parcel.obtain();
+            data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+            data.writeString(BroadcastTest.RECEIVER_REMOTE);
+            caller.transact(BroadcastTest.GOT_RECEIVE_TRANSACTION, data, null, 0);
+            data.recycle();
+        } catch (RemoteException ex) {
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteReceiver.java b/core/tests/coretests/src/android/app/activity/RemoteReceiver.java
new file mode 100644
index 0000000..9608fc4
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteReceiver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+
+public class RemoteReceiver extends BroadcastReceiver
+{
+    public RemoteReceiver()
+    {
+    }
+
+    public void onReceive(Context context, Intent intent)
+    {
+        if (LaunchpadActivity.BROADCAST_REPEAT.equals(intent.getAction())) {
+            Intent newIntent = new Intent(intent);
+            newIntent.setAction(LaunchpadActivity.BROADCAST_REMOTE);
+            context.sendOrderedBroadcast(newIntent, null);
+        }
+        try {
+            IBinder caller = intent.getIBinderExtra("caller");
+            Parcel data = Parcel.obtain();
+            data.writeInterfaceToken(LaunchpadActivity.LAUNCH);
+            data.writeString(LaunchpadActivity.RECEIVER_REMOTE);
+            caller.transact(LaunchpadActivity.GOT_RECEIVE_TRANSACTION, data, null, 0);
+            data.recycle();
+        } catch (RemoteException ex) {
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
new file mode 100644
index 0000000..e969d10
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/RemoteSubActivityScreen.java
@@ -0,0 +1,59 @@
+/* //device/apps/AndroidTests/src/com.android.unit_tests/activity/TestedScreen.java
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT 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.activity;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Process;
+import android.util.Log;
+
+public class RemoteSubActivityScreen extends SubActivityScreen {
+	Handler mHandler = new Handler();
+	boolean mFirst = false;
+
+    public RemoteSubActivityScreen() {
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+    	// We are running in a remote process, so want to have the sub-activity
+    	// sending the result back in the original process.
+        Intent intent = getIntent();
+    	intent.setClass(this, SubActivityScreen.class);
+    	
+        super.onCreate(icicle);
+        
+        boolean kill = intent.getBooleanExtra("kill", false);
+        //Log.i("foo", "RemoteSubActivityScreen pid=" + Process.myPid()
+        //        + " kill=" + kill);
+        
+        if (kill) {
+	        // After finishing initialization, kill the process!  But only if
+	        // this is the first time...
+	        if (icicle == null) {
+		        mHandler.post(new Runnable() {
+		        	public void run() {
+		        		handleBeforeStopping();
+		        		Process.killProcess(Process.myPid());
+		        	}
+		        });
+	        }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ResultReceiver.java b/core/tests/coretests/src/android/app/activity/ResultReceiver.java
new file mode 100644
index 0000000..f7daf2c
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ResultReceiver.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Bundle;
+
+import java.util.Map;
+
+public class ResultReceiver extends BroadcastReceiver
+{
+    public ResultReceiver()
+    {
+    }
+
+    public void onReceive(Context context, Intent intent)
+    {
+        setResultCode(3);
+        setResultData("bar");
+        Bundle map = getResultExtras(false);
+        map.remove("remove");
+        map.putString("bar", "them");
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/SearchableActivity.java b/core/tests/coretests/src/android/app/activity/SearchableActivity.java
new file mode 100644
index 0000000..e238572
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SearchableActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.app.activity;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SearchableActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        finish();
+    }
+
+}
diff --git a/core/tests/coretests/src/android/app/activity/ServiceTest.java b/core/tests/coretests/src/android/app/activity/ServiceTest.java
new file mode 100644
index 0000000..d3ae415
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/ServiceTest.java
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+// These test binders purport to support an interface whose canonical
+// interface name is ServiceTest.SERVICE_LOCAL
+// Temporarily suppress, this test is causing unit test suite run to fail
+// TODO: remove this suppress
+@Suppress
+public class ServiceTest extends ActivityTestsBase {
+
+    public static final String SERVICE_LOCAL =
+            "com.android.frameworks.coretests.activity.SERVICE_LOCAL";
+    public static final String SERVICE_LOCAL_GRANTED =
+            "com.android.frameworks.coretests.activity.SERVICE_LOCAL_GRANTED";
+    public static final String SERVICE_LOCAL_DENIED =
+            "com.android.frameworks.coretests.activity.SERVICE_LOCAL_DENIED";
+
+    public static final String REPORT_OBJ_NAME = "report";
+
+    public static final int STARTED_CODE = 1;
+    public static final int DESTROYED_CODE = 2;
+    public static final int SET_REPORTER_CODE = 3;
+    public static final int UNBIND_CODE = 4;
+    public static final int REBIND_CODE = 5;
+
+    public static final int STATE_START_1 = 0;
+    public static final int STATE_START_2 = 1;
+    public static final int STATE_UNBIND = 2;
+    public static final int STATE_DESTROY = 3;
+    public static final int STATE_REBIND = 4;
+    public static final int STATE_UNBIND_ONLY = 5;
+    public int mStartState;
+
+    public IBinder mStartReceiver = new Binder() {
+        @Override
+        protected boolean onTransact(int code, Parcel data, Parcel reply,
+                int flags) throws RemoteException {
+            //Log.i("ServiceTest", "Received code " + code + " in state " + mStartState);
+            if (code == STARTED_CODE) {
+                data.enforceInterface(SERVICE_LOCAL);
+                int count = data.readInt();
+                if (mStartState == STATE_START_1) {
+                    if (count == 1) {
+                        finishGood();
+                    } else {
+                        finishBad("onStart() again on an object when it should have been the first time");
+                    }
+                } else if (mStartState == STATE_START_2) {
+                    if (count == 2) {
+                        finishGood();
+                    } else {
+                        finishBad("onStart() the first time on an object when it should have been the second time");
+                    }
+                } else {
+                    finishBad("onStart() was called when not expected (state="+mStartState+")");
+                }
+                return true;
+            } else if (code == DESTROYED_CODE) {
+                data.enforceInterface(SERVICE_LOCAL);
+                if (mStartState == STATE_DESTROY) {
+                    finishGood();
+                } else {
+                    finishBad("onDestroy() was called when not expected (state="+mStartState+")");
+                }
+                return true;
+            } else if (code == UNBIND_CODE) {
+                data.enforceInterface(SERVICE_LOCAL);
+                if (mStartState == STATE_UNBIND) {
+                    mStartState = STATE_DESTROY;
+                } else if (mStartState == STATE_UNBIND_ONLY) {
+                    finishGood();
+                } else {
+                    finishBad("onUnbind() was called when not expected (state="+mStartState+")");
+                }
+                return true;
+            } else if (code == REBIND_CODE) {
+                data.enforceInterface(SERVICE_LOCAL);
+                if (mStartState == STATE_REBIND) {
+                    finishGood();
+                } else {
+                    finishBad("onRebind() was called when not expected (state="+mStartState+")");
+                }
+                return true;
+            } else {
+                return super.onTransact(code, data, reply, flags);
+            }
+        }
+    };
+
+    public class EmptyConnection implements ServiceConnection {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+        }
+    }
+
+    public class TestConnection implements ServiceConnection {
+        private final boolean mExpectDisconnect;
+        private final boolean mSetReporter;
+        private boolean mMonitor;
+        private int mCount;
+
+        public TestConnection(boolean expectDisconnect, boolean setReporter) {
+            mExpectDisconnect = expectDisconnect;
+            mSetReporter = setReporter;
+            mMonitor = !setReporter;
+        }
+
+        void setMonitor(boolean v) {
+            mMonitor = v;
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (mSetReporter) {
+                Parcel data = Parcel.obtain();
+                data.writeInterfaceToken(SERVICE_LOCAL);
+                data.writeStrongBinder(mStartReceiver);
+                try {
+                    service.transact(SET_REPORTER_CODE, data, null, 0);
+                } catch (RemoteException e) {
+                    finishBad("DeadObjectException when sending reporting object");
+                }
+                data.recycle();
+            }
+
+            if (mMonitor) {
+                mCount++;
+                if (mStartState == STATE_START_1) {
+                    if (mCount == 1) {
+                        finishGood();
+                    } else {
+                        finishBad("onServiceConnected() again on an object when it should have been the first time");
+                    }
+                } else if (mStartState == STATE_START_2) {
+                    if (mCount == 2) {
+                        finishGood();
+                    } else {
+                        finishBad("onServiceConnected() the first time on an object when it should have been the second time");
+                    }
+                } else {
+                    finishBad("onServiceConnected() called unexpectedly");
+                }
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            if (mMonitor) {
+                if (mStartState == STATE_DESTROY) {
+                    if (mExpectDisconnect) {
+                        finishGood();
+                    } else {
+                        finishBad("onServiceDisconnected() when it shouldn't have been");
+                    }
+                } else {
+                    finishBad("onServiceDisconnected() called unexpectedly");
+                }
+            }
+        }
+    }
+
+    void startExpectResult(Intent service) {
+        startExpectResult(service, new Bundle());
+    }
+
+    void startExpectResult(Intent service, Bundle bundle) {
+        bundle.putIBinder(REPORT_OBJ_NAME, mStartReceiver);
+        boolean success = false;
+        try {
+            //Log.i("foo", "STATE_START_1");
+            mStartState = STATE_START_1;
+            getContext().startService(new Intent(service).putExtras(bundle));
+            waitForResultOrThrow(5 * 1000, "service to start first time");
+            //Log.i("foo", "STATE_START_2");
+            mStartState = STATE_START_2;
+            getContext().startService(new Intent(service).putExtras(bundle));
+            waitForResultOrThrow(5 * 1000, "service to start second time");
+            success = true;
+        } finally {
+            if (!success) {
+                try {
+                    getContext().stopService(service);
+                } catch (Exception e) {
+                    // eat
+                }
+            }
+        }
+        //Log.i("foo", "STATE_DESTROY");
+        mStartState = STATE_DESTROY;
+        getContext().stopService(service);
+        waitForResultOrThrow(5 * 1000, "service to be destroyed");
+    }
+
+    void startExpectNoPermission(Intent service) {
+        try {
+            getContext().startService(service);
+            fail("Expected security exception when starting " + service);
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    void bindExpectResult(Intent service) {
+        TestConnection conn = new TestConnection(true, false);
+        TestConnection conn2 = new TestConnection(false, false);
+        boolean success = false;
+        try {
+            // Expect to see the TestConnection connected.
+            mStartState = STATE_START_1;
+            getContext().bindService(service, conn, 0);
+            getContext().startService(service);
+            waitForResultOrThrow(5 * 1000, "existing connection to receive service");
+
+            // Expect to see the second TestConnection connected.
+            getContext().bindService(service, conn2, 0);
+            waitForResultOrThrow(5 * 1000, "new connection to receive service");
+
+            getContext().unbindService(conn2);
+            success = true;
+        } finally {
+            if (!success) {
+                try {
+                    getContext().stopService(service);
+                    getContext().unbindService(conn);
+                    getContext().unbindService(conn2);
+                } catch (Exception e) {
+                    // eat
+                }
+            }
+        }
+
+        // Expect to see the TestConnection disconnected.
+        mStartState = STATE_DESTROY;
+        getContext().stopService(service);
+        waitForResultOrThrow(5 * 1000, "existing connection to lose service");
+
+        getContext().unbindService(conn);
+
+        conn = new TestConnection(true, true);
+        success = false;
+        try {
+            // Expect to see the TestConnection connected.
+            conn.setMonitor(true);
+            mStartState = STATE_START_1;
+            getContext().bindService(service, conn, 0);
+            getContext().startService(service);
+            waitForResultOrThrow(5 * 1000, "existing connection to receive service");
+
+            success = true;
+        } finally {
+            if (!success) {
+                try {
+                    getContext().stopService(service);
+                    getContext().unbindService(conn);
+                } catch (Exception e) {
+                    // eat
+                }
+            }
+        }
+
+        // Expect to see the service unbind and then destroyed.
+        conn.setMonitor(false);
+        mStartState = STATE_UNBIND;
+        getContext().stopService(service);
+        waitForResultOrThrow(5 * 1000, "existing connection to lose service");
+
+        getContext().unbindService(conn);
+
+        conn = new TestConnection(true, true);
+        success = false;
+        try {
+            // Expect to see the TestConnection connected.
+            conn.setMonitor(true);
+            mStartState = STATE_START_1;
+            getContext().bindService(service, conn, 0);
+            getContext().startService(service);
+            waitForResultOrThrow(5 * 1000, "existing connection to receive service");
+
+            success = true;
+        } finally {
+            if (!success) {
+                try {
+                    getContext().stopService(service);
+                    getContext().unbindService(conn);
+                } catch (Exception e) {
+                    // eat
+                }
+            }
+        }
+
+        // Expect to see the service unbind but not destroyed.
+        conn.setMonitor(false);
+        mStartState = STATE_UNBIND_ONLY;
+        getContext().unbindService(conn);
+        waitForResultOrThrow(5 * 1000, "existing connection to unbind service");
+
+        // Expect to see the service rebound.
+        mStartState = STATE_REBIND;
+        getContext().bindService(service, conn, 0);
+        waitForResultOrThrow(5 * 1000, "existing connection to rebind service");
+
+        // Expect to see the service unbind and then destroyed.
+        mStartState = STATE_UNBIND;
+        getContext().stopService(service);
+        waitForResultOrThrow(5 * 1000, "existing connection to lose service");
+
+        getContext().unbindService(conn);
+    }
+
+    void bindAutoExpectResult(Intent service) {
+        TestConnection conn = new TestConnection(false, true);
+        boolean success = false;
+        try {
+            conn.setMonitor(true);
+            mStartState = STATE_START_1;
+            getContext().bindService(
+                    service, conn, Context.BIND_AUTO_CREATE);
+            waitForResultOrThrow(5 * 1000, "connection to start and receive service");
+            success = true;
+        } finally {
+            if (!success) {
+                try {
+                    getContext().unbindService(conn);
+                } catch (Exception e) {
+                    // eat
+                }
+            }
+        }
+        mStartState = STATE_UNBIND;
+        getContext().unbindService(conn);
+        waitForResultOrThrow(5 * 1000, "disconnecting from service");
+    }
+
+    void bindExpectNoPermission(Intent service) {
+        TestConnection conn = new TestConnection(false, false);
+        try {
+            getContext().bindService(service, conn, Context.BIND_AUTO_CREATE);
+            fail("Expected security exception when binding " + service);
+        } catch (SecurityException e) {
+            // expected
+        } finally {
+            getContext().unbindService(conn);
+        }
+    }
+
+
+    @MediumTest
+    public void testLocalStartClass() throws Exception {
+        startExpectResult(new Intent(getContext(), LocalService.class));
+    }
+
+    @MediumTest
+    public void testLocalStartAction() throws Exception {
+        startExpectResult(new Intent(SERVICE_LOCAL));
+    }
+
+    @MediumTest
+    public void testLocalBindClass() throws Exception {
+        bindExpectResult(new Intent(getContext(), LocalService.class));
+    }
+
+    @MediumTest
+    public void testLocalBindAction() throws Exception {
+        bindExpectResult(new Intent(SERVICE_LOCAL));
+    }
+
+    @MediumTest
+    public void testLocalBindAutoClass() throws Exception {
+        bindAutoExpectResult(new Intent(getContext(), LocalService.class));
+    }
+
+    @MediumTest
+    public void testLocalBindAutoAction() throws Exception {
+        bindAutoExpectResult(new Intent(SERVICE_LOCAL));
+    }
+
+    @MediumTest
+    public void testLocalStartClassPermissionGranted() throws Exception {
+        startExpectResult(new Intent(getContext(), LocalGrantedService.class));
+    }
+
+    @MediumTest
+    public void testLocalStartActionPermissionGranted() throws Exception {
+        startExpectResult(new Intent(SERVICE_LOCAL_GRANTED));
+    }
+
+    @MediumTest
+    public void testLocalBindClassPermissionGranted() throws Exception {
+        bindExpectResult(new Intent(getContext(), LocalGrantedService.class));
+    }
+
+    @MediumTest
+    public void testLocalBindActionPermissionGranted() throws Exception {
+        bindExpectResult(new Intent(SERVICE_LOCAL_GRANTED));
+    }
+
+    @MediumTest
+    public void testLocalBindAutoClassPermissionGranted() throws Exception {
+        bindAutoExpectResult(new Intent(getContext(), LocalGrantedService.class));
+    }
+
+    @MediumTest
+    public void testLocalBindAutoActionPermissionGranted() throws Exception {
+        bindAutoExpectResult(new Intent(SERVICE_LOCAL_GRANTED));
+    }
+
+    @MediumTest
+    public void testLocalStartClassPermissionDenied() throws Exception {
+        startExpectNoPermission(new Intent(getContext(), LocalDeniedService.class));
+    }
+
+    @MediumTest
+    public void testLocalStartActionPermissionDenied() throws Exception {
+        startExpectNoPermission(new Intent(SERVICE_LOCAL_DENIED));
+    }
+
+    @MediumTest
+    public void testLocalBindClassPermissionDenied() throws Exception {
+        bindExpectNoPermission(new Intent(getContext(), LocalDeniedService.class));
+    }
+
+    @MediumTest
+    public void testLocalBindActionPermissionDenied() throws Exception {
+        bindExpectNoPermission(new Intent(SERVICE_LOCAL_DENIED));
+    }
+
+    @MediumTest
+    public void testLocalUnbindTwice() throws Exception {
+        EmptyConnection conn = new EmptyConnection();
+        getContext().bindService(
+                new Intent(SERVICE_LOCAL_GRANTED), conn, 0);
+        getContext().unbindService(conn);
+        try {
+            getContext().unbindService(conn);
+            fail("No exception thrown on second unbind");
+        } catch (IllegalArgumentException e) {
+            //Log.i("foo", "Unbind exception", e);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
new file mode 100644
index 0000000..41b9547
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SetTimeZonePermissionsTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.activity;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.util.TimeZone;
+
+public class SetTimeZonePermissionsTest extends AndroidTestCase {
+
+    private String[] mZones;
+    private String mCurrentZone;
+    private AlarmManager mAlarm;
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mZones = TimeZone.getAvailableIDs();
+        mCurrentZone = TimeZone.getDefault().getID();
+        mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+    }
+
+    /**
+     * Verify that non-system processes cannot set the time zone.
+     */
+    @LargeTest
+    public void testSetTimeZonePermissions() {
+        /**
+         * Attempt to set several predefined time zones, verifying that the system
+         * system default time zone has not actually changed from its prior state
+         * after each attempt.
+         */
+        int max = (mZones.length > 10) ? mZones.length : 10;
+        assertTrue("No system-defined time zones - test invalid", max > 0);
+
+        for (int i = 0; i < max; i++) {
+            String tz = mZones[i];
+            try {
+                mAlarm.setTimeZone(tz);
+            } catch (SecurityException se) {
+                // Expected failure; no need to handle specially since we're
+                // about to assert that the test invariant holds: no change
+                // to the system time zone.
+            }
+
+            String newZone = TimeZone.getDefault().getID();
+            assertEquals("AlarmManager.setTimeZone() succeeded despite lack of permission",
+                    mCurrentZone,
+                    newZone);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/SubActivityScreen.java b/core/tests/coretests/src/android/app/activity/SubActivityScreen.java
new file mode 100644
index 0000000..919c591
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SubActivityScreen.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class SubActivityScreen extends Activity {
+    static final int NO_RESULT_MODE = 0;
+    static final int RESULT_MODE = 1;
+    static final int PENDING_RESULT_MODE = 2;
+    static final int FINISH_SUB_MODE = 3;
+
+    static final int CHILD_OFFSET = 1000;
+
+    int mMode;
+
+    public SubActivityScreen() {
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        mMode = getIntent().getIntExtra("mode", mMode);
+        //Log.i("foo", "SubActivityScreen pid=" + Process.myPid()
+        //        + " mode=" + mMode);
+        
+        // Move on to the next thing that will generate a result...  but only
+        // if we are being launched for the first time.
+        if (icicle == null) {
+	        if (mMode == PENDING_RESULT_MODE) {
+	            PendingIntent apr = createPendingResult(1, null,
+	                    Intent.FILL_IN_ACTION);
+	            Intent res = new Intent();
+                res.putExtra("tkey", "tval");
+                res.setAction("test");
+	            try {
+    	            apr.send(this, RESULT_OK, res);
+	            } catch (PendingIntent.CanceledException e) {
+	            }
+	        } else if (mMode < CHILD_OFFSET) {
+	            Intent intent = new Intent();
+	        	intent.setClass(this, SubActivityScreen.class);
+	            intent.putExtra("mode", CHILD_OFFSET+mMode);
+	            //System.out.println("*** Starting from onStart: " + intent);
+	            startActivityForResult(intent, 1);
+	            return;
+	        }
+        }
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle state) {
+        super.onRestoreInstanceState(state);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        //Log.i("foo", "SubActivityScreen pid=" + Process.myPid() + " onResume");
+        
+        if (mMode >= CHILD_OFFSET) {
+        	// Wait a little bit, to give our parent time to kill itself
+        	// if that is something it is into.
+        	try {
+	        	Thread.sleep(500);
+        	} catch (InterruptedException e) {
+        		setResult(RESULT_CANCELED, (new Intent()).setAction("Interrupted!"));
+        		finish();
+        		return;
+        	}
+            //System.out.println("Resuming sub-activity: mode=" + mMode);
+            switch (mMode-CHILD_OFFSET) {
+            case NO_RESULT_MODE:
+                finish();
+                break;
+            case RESULT_MODE:
+                Intent res = new Intent();
+                res.putExtra("tkey", "tval");
+                res.setAction("test");
+                setResult(RESULT_OK, res);
+                finish();
+                break;
+            case FINISH_SUB_MODE:
+                break;
+            }
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode,
+            Intent data) {
+        //Log.i("foo", "SubActivityScreen pid=" + Process.myPid()
+        //        + " onActivityResult: req=" + requestCode
+        //        + " res=" + resultCode);
+
+        // Assume success.
+        setResult(RESULT_OK);
+
+        if (requestCode == 1) {
+            switch (mMode) {
+            case NO_RESULT_MODE:
+            case FINISH_SUB_MODE:
+                if (resultCode != RESULT_CANCELED) {
+                    setResult(RESULT_CANCELED, (new Intent()).setAction(
+                            "Incorrect result code returned: " + resultCode));
+                }
+                break;
+            case RESULT_MODE:
+            case PENDING_RESULT_MODE:
+                if (resultCode != RESULT_OK) {
+                    setResult(RESULT_CANCELED, (new Intent()).setAction(
+                            "Incorrect result code returned: " + resultCode));
+                } else if (data == null) {
+                    setResult(RESULT_CANCELED, (new Intent()).setAction(
+                            "null data returned"));
+                } else if (!("test".equals(data.getAction()))) {
+                    setResult(RESULT_CANCELED, (new Intent()).setAction(
+                            "Incorrect action returned: " + data));
+                } else if (!("tval".equals(data.getStringExtra("tkey")))) {
+                    setResult(RESULT_CANCELED, (new Intent()).setAction(
+                            "Incorrect extras returned: " + data.getExtras()));
+                }
+                break;
+            }
+        } else {
+            setResult(RESULT_CANCELED, (new Intent()).setAction(
+                    "Incorrect request code returned: " + requestCode));
+        }
+
+        finish();
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        handleBeforeStopping();
+    }
+    
+    public void handleBeforeStopping() {
+        if (mMode == FINISH_SUB_MODE) {
+            finishActivity(1);
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/SubActivityTest.java b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
new file mode 100644
index 0000000..35dde8a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/SubActivityTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.test.suitebuilder.annotation.Suppress;
+import android.content.ComponentName;
+
+@Suppress
+public class SubActivityTest extends ActivityTestsBase {
+
+    public void testPendingResult() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.PENDING_RESULT_MODE);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testNoResult() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.NO_RESULT_MODE);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testResult() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.RESULT_MODE);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testFinishSub() throws Exception {
+        mIntent.putExtra("component",
+                new ComponentName(getContext(), RemoteSubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.FINISH_SUB_MODE);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testRemoteNoResult() throws Exception {
+        mIntent.putExtra("component",
+                new ComponentName(getContext(), RemoteSubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.NO_RESULT_MODE);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testRemoteResult() throws Exception {
+        mIntent.putExtra("component",
+                new ComponentName(getContext(), RemoteSubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.RESULT_MODE);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testRemoteFinishSub() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.FINISH_SUB_MODE);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testRemoteRestartNoResult() throws Exception {
+        mIntent.putExtra("component",
+                new ComponentName(getContext(), RemoteSubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.NO_RESULT_MODE);
+        mIntent.putExtra("kill", true);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testRemoteRestartResult() throws Exception {
+        mIntent.putExtra("component",
+                new ComponentName(getContext(), RemoteSubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.RESULT_MODE);
+        mIntent.putExtra("kill", true);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+
+    public void testRemoteRestartFinishSub() throws Exception {
+        mIntent.putExtra("component", new ComponentName(getContext(), SubActivityScreen.class));
+        mIntent.putExtra("mode", SubActivityScreen.FINISH_SUB_MODE);
+        mIntent.putExtra("kill", true);
+        runLaunchpad(LaunchpadActivity.LAUNCH);
+    }
+}
diff --git a/core/tests/coretests/src/android/app/activity/TestedActivity.java b/core/tests/coretests/src/android/app/activity/TestedActivity.java
new file mode 100644
index 0000000..3a1c15f
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/TestedActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.Bundle;
+
+public class TestedActivity extends Activity
+{
+    public TestedActivity()
+    {
+    }
+
+    public void onCreate(Bundle icicle)
+    {
+        super.onCreate(icicle);
+    }
+
+    protected void onRestoreInstanceState(Bundle state)
+    {
+        super.onRestoreInstanceState(state);
+    }
+
+    protected void onResume()
+    {
+        super.onResume();
+        Looper.myLooper().myQueue().addIdleHandler(new Idler());
+    }
+
+    protected void onSaveInstanceState(Bundle outState)
+    {
+        super.onSaveInstanceState(outState);
+    }
+
+    protected void onStop()
+    {
+        super.onStop();
+    }
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            setResult(RESULT_OK);
+            finish();
+        }
+    };
+
+    private class Idler implements MessageQueue.IdleHandler
+    {
+        public final boolean queueIdle()
+        {
+            //Message m = Message.obtain();
+            //mHandler.sendMessageAtTime(m, SystemClock.uptimeMillis()+1000);
+            setResult(RESULT_OK);
+            finish();
+            return false;
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/app/activity/TestedScreen.java b/core/tests/coretests/src/android/app/activity/TestedScreen.java
new file mode 100644
index 0000000..1682d1a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/activity/TestedScreen.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.activity;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.SystemClock;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestedScreen extends Activity
+{
+    public static final String WAIT_BEFORE_FINISH = "TestedScreen.WAIT_BEFORE_FINISH";
+    public static final String DELIVER_RESULT = "TestedScreen.DELIVER_RESULT";
+    public static final String CLEAR_TASK = "TestedScreen.CLEAR_TASK";
+    
+    public TestedScreen() {
+    }
+
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "CREATE tested "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        if (LaunchpadActivity.FORWARD_RESULT.equals(getIntent().getAction())) {
+            Intent intent = new Intent(getIntent());
+            intent.setAction(DELIVER_RESULT);
+            intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+            startActivity(intent);
+            if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+                    + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+            finish();
+        } else if (DELIVER_RESULT.equals(getIntent().getAction())) {
+            setResult(RESULT_OK, (new Intent()).setAction(
+                    LaunchpadActivity.RETURNED_RESULT));
+            if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+                    + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+            finish();
+        } else if (CLEAR_TASK.equals(getIntent().getAction())) {
+            if (!getIntent().getBooleanExtra(ClearTop.WAIT_CLEAR_TASK, false)) {
+                launchClearTask();
+            }
+        }
+    }
+
+    protected void onRestoreInstanceState(Bundle state) {
+        super.onRestoreInstanceState(state);
+    }
+
+    protected void onResume() {
+        super.onResume();
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "RESUME tested "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+        if (CLEAR_TASK.equals(getIntent().getAction())) {
+            if (getIntent().getBooleanExtra(ClearTop.WAIT_CLEAR_TASK, false)) {
+                Looper.myLooper().myQueue().addIdleHandler(new Idler());
+            }
+        } else {
+            Looper.myLooper().myQueue().addIdleHandler(new Idler());
+        }
+    }
+
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+    }
+
+    protected void onStop() {
+        super.onStop();
+        if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "STOP tested "
+                + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+    }
+
+    private void launchClearTask() {
+        Intent intent = new Intent(getIntent()).
+        addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).
+        setClass(this, ClearTop.class);
+        startActivity(intent);
+    }
+    
+    private Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            if (CLEAR_TASK.equals(getIntent().getAction())) {
+                launchClearTask();
+            } else {
+                if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+                        + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+                setResult(RESULT_OK);
+                finish();
+            }
+        }
+    };
+
+    private class Idler implements MessageQueue.IdleHandler {
+        public final boolean queueIdle() {
+            if (WAIT_BEFORE_FINISH.equals(getIntent().getAction())) {
+                Message m = Message.obtain();
+                mHandler.sendMessageAtTime(m, SystemClock.uptimeMillis()+1000);
+            } else if (CLEAR_TASK.equals(getIntent().getAction())) {
+                Message m = Message.obtain();
+                mHandler.sendMessageAtTime(m, SystemClock.uptimeMillis()+1000);
+            } else {
+                if (ActivityTests.DEBUG_LIFECYCLE) Log.v("test", "Finishing tested "
+                        + Integer.toHexString(System.identityHashCode(this)) + ": " + getIntent());
+                setResult(RESULT_OK);
+                finish();
+            }
+            return false;
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/content/AssetTest.java b/core/tests/coretests/src/android/content/AssetTest.java
new file mode 100644
index 0000000..b66574c
--- /dev/null
+++ b/core/tests/coretests/src/android/content/AssetTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.content;
+
+import android.content.res.AssetManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class AssetTest extends AndroidTestCase {
+    private AssetManager mAssets;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mAssets = mContext.getAssets();
+    }
+
+    public static void verifyTextAsset(InputStream is) throws IOException {
+        String expectedString = "OneTwoThreeFourFiveSixSevenEightNineTen";
+        byte[] buffer = new byte[10];
+
+        int readCount;
+        int curIndex = 0;
+        while ((readCount = is.read(buffer, 0, buffer.length)) > 0) {
+            for (int i = 0; i < readCount; i++) {
+                assertEquals("At index " + curIndex
+                            + " expected " + expectedString.charAt(curIndex)
+                            + " but found " + ((char) buffer[i]),
+                        buffer[i], expectedString.charAt(curIndex));
+                curIndex++;
+            }
+        }
+
+        readCount = is.read(buffer, 0, buffer.length);
+        assertEquals("Reading end of buffer: expected readCount=-1 but got " + readCount,
+                -1, readCount);
+
+        readCount = is.read(buffer, buffer.length, 0);
+        assertEquals("Reading end of buffer length 0: expected readCount=0 but got " + readCount,
+                0, readCount);
+
+        is.close();
+    }
+
+    @SmallTest
+    public void testReadToEnd() throws Exception {
+        InputStream is = mAssets.open("text.txt");
+        verifyTextAsset(is);
+    }
+
+    // XXX failing
+    public void xxtestListDir() throws Exception {
+        String[] files = mAssets.list("");
+        assertEquals(1, files.length);
+        assertEquals("test.txt", files[0]);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/ContentQueryMapTest.java b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
new file mode 100644
index 0000000..d1b8c24
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentQueryMapTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.content;
+
+import android.content.ContentQueryMap;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.util.Observable;
+import java.util.Observer;
+
+/** Test of {@link ContentQueryMap} */
+public class ContentQueryMapTest extends AndroidTestCase {
+    /** Helper class to run test code in a new thread with a Looper. */
+    private abstract class LooperThread extends Thread {
+        public Throwable mError = null;
+        public boolean mSuccess = false;
+
+        abstract void go();
+
+        public void run() {
+            try {
+                Looper.prepare();
+                go();
+                Looper.loop();
+            } catch (Throwable e) {
+                mError = e;
+            }
+        }
+    }
+
+    @MediumTest
+    public void testContentQueryMap() throws Throwable {
+        LooperThread thread = new LooperThread() {
+            void go() {
+                ContentResolver r = getContext().getContentResolver();
+                Settings.System.putString(r, "test", "Value");
+                Cursor cursor = r.query(
+                        Settings.System.CONTENT_URI,
+                        new String[] {
+                            Settings.System.NAME,
+                            Settings.System.VALUE,
+                        }, null, null, null);
+
+                final ContentQueryMap cqm = new ContentQueryMap(
+                        cursor, Settings.System.NAME, true, null);
+                // Get the current state of the CQM. This forces a requery and means that the
+                // call to getValues() below won't do a requery().
+                cqm.getRows();
+                
+                // The cache won't notice changes until the loop runs.
+                Settings.System.putString(r, "test", "New Value");
+                ContentValues v = cqm.getValues("test");
+                String value = v.getAsString(Settings.System.VALUE);
+                assertEquals("Value", value);
+
+                // Use an Observer to find out when the cache does update.
+                cqm.addObserver(new Observer() {
+                    public void update(Observable o, Object arg) {
+                        // Should have the new values by now.
+                        ContentValues v = cqm.getValues("test");
+                        String value = v.getAsString(Settings.System.VALUE);
+                        assertEquals("New Value", value);
+                        Looper.myLooper().quit();
+                        cqm.close();
+                        mSuccess = true;
+                    }
+                });
+
+                // Give up after a few seconds, if it doesn't.
+                new Handler().postDelayed(new Runnable() {
+                    public void run() {
+                        fail("Timed out");
+                    }
+                }, 5000);
+            }
+        };
+
+        thread.start();
+        thread.join();
+        if (thread.mError != null) throw thread.mError;
+        assertTrue(thread.mSuccess);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/ContentTests.java b/core/tests/coretests/src/android/content/ContentTests.java
new file mode 100644
index 0000000..a1299e3
--- /dev/null
+++ b/core/tests/coretests/src/android/content/ContentTests.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import junit.framework.TestSuite;
+
+public class ContentTests {
+    public static TestSuite suite() {
+        TestSuite suite = new TestSuite(ContentTests.class.getName());
+
+        suite.addTestSuite(AssetTest.class);
+        return suite;
+    }
+}
diff --git a/core/tests/coretests/src/android/content/MemoryFileProvider.java b/core/tests/coretests/src/android/content/MemoryFileProvider.java
new file mode 100644
index 0000000..c4bc767
--- /dev/null
+++ b/core/tests/coretests/src/android/content/MemoryFileProvider.java
@@ -0,0 +1,211 @@
+/*
+ * 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.content;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/** Simple test provider that runs in the local process. */
+public class MemoryFileProvider extends ContentProvider {
+    private static final String TAG = "MemoryFileProvider";
+
+    private static final String DATA_FILE = "data.bin";
+
+    // some random data
+    public static final byte[] TEST_BLOB = new byte[] {
+        -12,  127, 0, 3, 1, 2, 3, 4, 5, 6, 1, -128, -1, -54, -65, 35,
+        -53, -96, -74, -74, -55, -43, -69, 3, 52, -58,
+        -121, 127, 87, -73, 16, -13, -103, -65, -128, -36,
+        107, 24, 118, -17, 97, 97, -88, 19, -94, -54,
+        53, 43, 44, -27, -124, 28, -74, 26, 35, -36,
+        16, -124, -31, -31, -128, -79, 108, 116, 43, -17 };
+
+    private SQLiteOpenHelper mOpenHelper;
+
+    private static final int DATA_ID_BLOB = 1;
+    private static final int HUGE = 2;
+    private static final int FILE = 3;
+
+    private static final UriMatcher sURLMatcher = new UriMatcher(
+            UriMatcher.NO_MATCH);
+
+    static {
+        sURLMatcher.addURI("*", "data/#/blob", DATA_ID_BLOB);
+        sURLMatcher.addURI("*", "huge", HUGE);
+        sURLMatcher.addURI("*", "file", FILE);
+    }
+
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+        private static final String DATABASE_NAME = "local.db";
+        private static final int DATABASE_VERSION = 1;
+
+        public DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE data (" +
+                       "_id INTEGER PRIMARY KEY," +
+                       "_blob TEXT, " +
+                       "integer INTEGER);");
+
+            // insert alarms
+            ContentValues values = new ContentValues();
+            values.put("_id", 1);
+            values.put("_blob", TEST_BLOB);
+            values.put("integer", 100);
+            db.insert("data", null, values);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+            Log.w(TAG, "Upgrading test database from version " +
+                  oldVersion + " to " + currentVersion +
+                  ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS data");
+            onCreate(db);
+        }
+    }
+
+
+    public MemoryFileProvider() {
+    }
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        try {
+            OutputStream out = getContext().openFileOutput(DATA_FILE, Context.MODE_PRIVATE);
+            out.write(TEST_BLOB);
+            out.close();
+        } catch (IOException ex) {
+            ex.printStackTrace();
+        }
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri url, String[] projectionIn, String selection,
+            String[] selectionArgs, String sort) {
+        throw new UnsupportedOperationException("query not supported");
+    }
+
+    @Override
+    public String getType(Uri url) {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case DATA_ID_BLOB:
+                return "application/octet-stream";
+            case FILE:
+                return "application/octet-stream";
+            default:
+                throw new IllegalArgumentException("Unknown URL");
+        }
+    }
+
+    @Override
+    public AssetFileDescriptor openAssetFile(Uri url, String mode) throws FileNotFoundException {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case DATA_ID_BLOB:
+                String sql = "SELECT _blob FROM data WHERE _id=" + url.getPathSegments().get(1);
+                return getBlobColumnAsAssetFile(url, mode, sql);
+            case HUGE:
+                try {
+                    MemoryFile memoryFile = new MemoryFile(null, 5000000);
+                    memoryFile.writeBytes(TEST_BLOB, 0, 1000000, TEST_BLOB.length);
+                    memoryFile.deactivate();
+                    return AssetFileDescriptor.fromMemoryFile(memoryFile);
+                } catch (IOException ex) {
+                    throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
+                }
+            case FILE:
+                File file = getContext().getFileStreamPath(DATA_FILE);
+                ParcelFileDescriptor fd =
+                        ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+                return new AssetFileDescriptor(fd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
+            default:
+                throw new FileNotFoundException("No files supported by provider at " + url);
+        }
+    }
+
+    private AssetFileDescriptor getBlobColumnAsAssetFile(Uri url, String mode, String sql)
+            throws FileNotFoundException {
+        if (!"r".equals(mode)) {
+            throw new FileNotFoundException("Mode " + mode + " not supported for " + url);
+        }
+        try {
+            SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+            MemoryFile file = simpleQueryForBlobMemoryFile(db, sql);
+            if (file == null) throw new FileNotFoundException("No such entry: " + url);
+            AssetFileDescriptor afd = AssetFileDescriptor.fromMemoryFile(file);
+            file.deactivate();
+            // need to dup and then close? openFileHelper() doesn't do that though
+            return afd;
+        } catch (IOException ex) {
+            throw new FileNotFoundException("Error reading " + url + ":" + ex.toString());
+        }
+    }
+
+    private MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql) throws IOException {
+        Cursor cursor = db.rawQuery(sql, null);
+        try {
+            if (!cursor.moveToFirst()) {
+                return null;
+            }
+            byte[] bytes = cursor.getBlob(0);
+            MemoryFile file = new MemoryFile(null, bytes.length);
+            file.writeBytes(bytes, 0, 0, bytes.length);
+            return file;
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    @Override
+    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+        throw new UnsupportedOperationException("update not supported");
+    }
+
+    @Override
+    public Uri insert(Uri url, ContentValues initialValues) {
+        throw new UnsupportedOperationException("insert not supported");
+    }
+
+    @Override
+    public int delete(Uri url, String where, String[] whereArgs) {
+        throw new UnsupportedOperationException("delete not supported");
+    }
+}
diff --git a/core/tests/coretests/src/android/content/MemoryFileProviderTest.java b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
new file mode 100644
index 0000000..6708af6e
--- /dev/null
+++ b/core/tests/coretests/src/android/content/MemoryFileProviderTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.content;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.InputStream;
+import java.util.Arrays;
+
+/**
+ * Tests reading a MemoryFile-based AssestFile from a ContentProvider running
+ * in a different process.
+ */
+public class MemoryFileProviderTest extends AndroidTestCase {
+
+    // reads from a cross-process AssetFileDescriptor for a MemoryFile
+    @MediumTest
+    public void testRead() throws Exception {
+        ContentResolver resolver = getContext().getContentResolver();
+        Uri uri = Uri.parse("content://android.content.MemoryFileProvider/data/1/blob");
+        byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+        InputStream in = resolver.openInputStream(uri);
+        assertNotNull(in);
+        int count = in.read(buf);
+        assertEquals(buf.length, count);
+        assertEquals(-1, in.read());
+        in.close();
+        assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+    }
+
+    // tests that we don't leak file descriptors or virtual address space
+    @MediumTest
+    public void testClose() throws Exception {
+        ContentResolver resolver = getContext().getContentResolver();
+        // open enough file descriptors that we will crash something if we leak FDs
+        // or address space
+        for (int i = 0; i < 1025; i++) {
+            Uri uri = Uri.parse("content://android.content.MemoryFileProvider/huge");
+            InputStream in = resolver.openInputStream(uri);
+            assertNotNull("Failed to open stream number " + i, in);
+            assertEquals(1000000, in.skip(1000000));
+            byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+            int count = in.read(buf);
+            assertEquals(buf.length, count);
+            assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+            in.close();
+        }
+    }
+
+    // tests that we haven't broken AssestFileDescriptors for normal files.
+    @MediumTest
+    public void testFile() throws Exception {
+        ContentResolver resolver = getContext().getContentResolver();
+        Uri uri = Uri.parse("content://android.content.MemoryFileProvider/file");
+        byte[] buf = new byte[MemoryFileProvider.TEST_BLOB.length];
+        InputStream in = resolver.openInputStream(uri);
+        assertNotNull(in);
+        int count = in.read(buf);
+        assertEquals(buf.length, count);
+        assertEquals(-1, in.read());
+        in.close();
+        assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
+    }
+
+}
diff --git a/core/tests/coretests/src/android/database/CursorWindowTest.java b/core/tests/coretests/src/android/database/CursorWindowTest.java
new file mode 100644
index 0000000..07e75cb
--- /dev/null
+++ b/core/tests/coretests/src/android/database/CursorWindowTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.database;
+
+import android.database.AbstractCursor;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.common.ArrayListCursor;
+import android.database.CursorWindow;
+import android.test.PerformanceTestCase;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+public class CursorWindowTest extends TestCase implements PerformanceTestCase {
+    public boolean isPerformanceOnly() {
+        return false;
+    }
+
+    // These test can only be run once.
+    public int startPerformance(Intermediates intermediates) {
+        return 1;
+    }
+
+    @SmallTest
+    public void testWriteCursorToWindow() throws Exception {
+        // create cursor
+        String[] colNames = new String[]{"name", "number", "profit"};
+        int colsize = colNames.length;
+        ArrayList<ArrayList> list = createTestList(10, colsize);
+        AbstractCursor cursor = new ArrayListCursor(colNames, (ArrayList<ArrayList>) list);
+
+        // fill window
+        CursorWindow window = new CursorWindow(false);
+        cursor.fillWindow(0, window);
+
+        // read from cursor window
+        for (int i = 0; i < list.size(); i++) {
+            ArrayList<Integer> col = list.get(i);
+            for (int j = 0; j < colsize; j++) {
+                String s = window.getString(i, j);
+                int r2 = col.get(j);
+                int r1 = Integer.parseInt(s);
+                assertEquals(r2, r1);
+            }
+        }
+
+        // test cursor window handle startpos != 0 
+        window.clear();
+        cursor.fillWindow(1, window);
+        // read from cursor from window
+        for (int i = 1; i < list.size(); i++) {
+            ArrayList<Integer> col = list.get(i);
+            for (int j = 0; j < colsize; j++) {
+                String s = window.getString(i, j);
+                int r2 = col.get(j);
+                int r1 = Integer.parseInt(s);
+                assertEquals(r2, r1);
+            }
+        }
+
+        // Clear the window and make sure it's empty
+        window.clear();
+        assertEquals(0, window.getNumRows());
+    }
+
+    @SmallTest
+    public void testValuesLocalWindow() {
+        doTestValues(new CursorWindow(true));
+    }
+    
+    @SmallTest
+    public void testValuesRemoteWindow() {
+        doTestValues(new CursorWindow(false));
+    }
+    
+    private void doTestValues(CursorWindow window) {
+        assertTrue(window.setNumColumns(7));
+        assertTrue(window.allocRow());
+        double db1 = 1.26;
+        assertTrue(window.putDouble(db1, 0, 0));
+        double db2 = window.getDouble(0, 0);
+        assertEquals(db1, db2);
+
+        long int1 = Long.MAX_VALUE;
+        assertTrue(window.putLong(int1, 0, 1));
+        long int2 = window.getLong(0, 1);
+        assertEquals(int1, int2);
+
+        assertTrue(window.putString("1198032740000", 0, 3));
+        assertEquals("1198032740000", window.getString(0, 3));
+        assertEquals(1198032740000L, window.getLong(0, 3));
+
+        assertTrue(window.putString(Long.toString(1198032740000L), 0, 3));
+        assertEquals(Long.toString(1198032740000L), window.getString(0, 3));
+        assertEquals(1198032740000L, window.getLong(0, 3));
+        
+        assertTrue(window.putString(Double.toString(42.0), 0, 4));
+        assertEquals(Double.toString(42.0), window.getString(0, 4));
+        assertEquals(42.0, window.getDouble(0, 4));
+        
+        // put blob
+        byte[] blob = new byte[1000];
+        byte value = 99;
+        Arrays.fill(blob, value);
+        assertTrue(window.putBlob(blob, 0, 6));
+        assertTrue(Arrays.equals(blob, window.getBlob(0, 6)));
+    }
+
+    @SmallTest
+    public void testNull() {
+        CursorWindow window = getOneByOneWindow();
+
+        // Put in a null value and read it back as various types
+        assertTrue(window.putNull(0, 0));
+        assertNull(window.getString(0, 0));
+        assertEquals(0, window.getLong(0, 0));
+        assertEquals(0.0, window.getDouble(0, 0));
+        assertNull(window.getBlob(0, 0));
+    }
+
+    @SmallTest
+    public void testEmptyString() {
+        CursorWindow window = getOneByOneWindow();
+
+        // put size 0 string and read it back as various types
+        assertTrue(window.putString("", 0, 0));
+        assertEquals("", window.getString(0, 0));
+        assertEquals(0, window.getLong(0, 0));
+        assertEquals(0.0, window.getDouble(0, 0));
+    }
+
+    private CursorWindow getOneByOneWindow() {
+        CursorWindow window = new CursorWindow(false);
+        assertTrue(window.setNumColumns(1));
+        assertTrue(window.allocRow());
+        return window;
+    }
+    
+    private static ArrayList<ArrayList> createTestList(int rows, int cols) {
+        ArrayList<ArrayList> list = Lists.newArrayList();
+        Random generator = new Random();
+
+        for (int i = 0; i < rows; i++) {
+            ArrayList<Integer> col = Lists.newArrayList();
+            list.add(col);
+            for (int j = 0; j < cols; j++) {
+                // generate random number
+                Integer r = generator.nextInt();
+                col.add(r);
+            }
+        }
+        return list;
+    }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseCursorTest.java b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
new file mode 100644
index 0000000..fad4349
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseCursorTest.java
@@ -0,0 +1,630 @@
+/*
+ * 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.
+ */
+
+package android.database;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.CursorIndexOutOfBoundsException;
+import android.database.DataSetObserver;
+import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteCursor;
+import android.database.sqlite.SQLiteCursorDriver;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteQuery;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTestCase {
+
+    private static final String sString1 = "this is a test";
+    private static final String sString2 = "and yet another test";
+    private static final String sString3 = "this string is a little longer, but still a test";
+
+    private static final int CURRENT_DATABASE_VERSION = 42;
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+	File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
+	mDatabaseFile = new File(dbDir, "database_test.db");
+
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    public boolean isPerformanceOnly() {
+        return false;
+    }
+
+    // These test can only be run once.
+    public int startPerformance(Intermediates intermediates) {
+        return 1;
+    }
+
+    private void populateDefaultTable() {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
+    }
+
+    @MediumTest
+    public void testCursorUpdate() {
+        mDatabase.execSQL(
+            "CREATE TABLE test (_id INTEGER PRIMARY KEY, d INTEGER, s INTEGER);");
+        for(int i = 0; i < 20; i++) {
+            mDatabase.execSQL("INSERT INTO test (d, s) VALUES (" + i + 
+                "," + i%2 + ");");
+        }
+        
+        Cursor c = mDatabase.query("test", null, "s = 0", null, null, null, null);
+        int dCol = c.getColumnIndexOrThrow("d");
+        int sCol = c.getColumnIndexOrThrow("s");
+        
+        int count = 0;
+        while (c.moveToNext()) {
+            assertTrue(c.updateInt(dCol, 3));
+            count++;
+        }
+        assertEquals(10, count);
+        
+        assertTrue(c.commitUpdates());
+        
+        assertTrue(c.requery());
+        
+        count = 0;
+        while (c.moveToNext()) {
+            assertEquals(3, c.getInt(dCol));
+            count++;
+        }
+        
+        assertEquals(10, count);
+        assertTrue(c.moveToFirst());
+        assertTrue(c.deleteRow());
+        assertEquals(9, c.getCount());
+        c.close();
+    }
+    
+    @MediumTest
+    public void testBlob() throws Exception {
+        // create table
+        mDatabase.execSQL(
+            "CREATE TABLE test (_id INTEGER PRIMARY KEY, s TEXT, d REAL, l INTEGER, b BLOB);");
+        // insert blob
+        Object[] args = new Object[4];
+        
+        byte[] blob = new byte[1000];
+        byte value = 99;
+        Arrays.fill(blob, value);        
+        args[3] = blob;
+        
+        String s = new String("text");        
+        args[0] = s;
+        Double d = 99.9;
+        args[1] = d;
+        Long l = (long)1000;
+        args[2] = l;
+        
+        String sql = "INSERT INTO test (s, d, l, b) VALUES (?,?,?,?)";
+        mDatabase.execSQL(sql, args);
+        // use cursor to access blob
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);        
+        c.moveToNext();
+        ContentValues cv = new ContentValues();
+        DatabaseUtils.cursorRowToContentValues(c, cv);
+        
+        int bCol = c.getColumnIndexOrThrow("b");
+        int sCol = c.getColumnIndexOrThrow("s");
+        int dCol = c.getColumnIndexOrThrow("d");
+        int lCol = c.getColumnIndexOrThrow("l");
+        byte[] cBlob =  c.getBlob(bCol);
+        assertTrue(Arrays.equals(blob, cBlob));
+        assertEquals(s, c.getString(sCol));
+        assertEquals((double)d, c.getDouble(dCol));
+        assertEquals((long)l, c.getLong(lCol));
+        
+        // new byte[]
+        byte[] newblob = new byte[1000];
+        value = 98;
+        Arrays.fill(blob, value);        
+        
+        c.updateBlob(bCol, newblob);
+        cBlob =  c.getBlob(bCol);
+        assertTrue(Arrays.equals(newblob, cBlob));
+        
+        // commit
+        assertTrue(c.commitUpdates());
+        assertTrue(c.requery());
+        c.moveToNext();
+        cBlob =  c.getBlob(bCol);
+        assertTrue(Arrays.equals(newblob, cBlob));        
+        c.close();
+    }
+    
+    @MediumTest
+    public void testRealColumns() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data REAL);");
+        ContentValues values = new ContentValues();
+        values.put("data", 42.11);
+        long id = mDatabase.insert("test", "data", values);
+        assertTrue(id > 0);
+        Cursor c = mDatabase.rawQuery("SELECT data FROM test", null);
+        assertNotNull(c);
+        assertTrue(c.moveToFirst());
+        assertEquals(42.11, c.getDouble(0));
+        c.close();
+    }
+
+    @MediumTest
+    public void testCursor1() throws Exception {
+        populateDefaultTable();
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+
+        int dataColumn = c.getColumnIndexOrThrow("data");
+
+        // The cursor should ignore text before the last period when looking for a column. (This
+        // is a temporary hack in all implementations of getColumnIndex.)
+        int dataColumn2 = c.getColumnIndexOrThrow("junk.data");
+        assertEquals(dataColumn, dataColumn2);
+
+        assertSame(3, c.getCount());
+
+        assertTrue(c.isBeforeFirst());
+
+        try {
+            c.getInt(0);
+            fail("CursorIndexOutOfBoundsException expected");
+        } catch (CursorIndexOutOfBoundsException ex) {
+            // expected
+        }
+
+        c.moveToNext();
+        assertEquals(1, c.getInt(0));
+
+        String s = c.getString(dataColumn);
+        assertEquals(sString1, s);
+
+        c.moveToNext();
+        s = c.getString(dataColumn);
+        assertEquals(sString2, s);
+
+        c.moveToNext();
+        s = c.getString(dataColumn);
+        assertEquals(sString3, s);
+
+        c.moveToPosition(-1);
+        c.moveToNext();
+        s = c.getString(dataColumn);
+        assertEquals(sString1, s);
+
+        c.moveToPosition(2);
+        s = c.getString(dataColumn);
+        assertEquals(sString3, s);
+
+        int i;
+
+        for (c.moveToFirst(), i = 0; !c.isAfterLast(); c.moveToNext(), i++) {
+            c.getInt(0);
+        }
+
+        assertEquals(3, i);
+
+        try {
+            c.getInt(0);
+            fail("CursorIndexOutOfBoundsException expected");
+        } catch (CursorIndexOutOfBoundsException ex) {
+            // expected
+        }
+        c.close();
+    }
+
+    @MediumTest
+    public void testCursor2() throws Exception {
+        populateDefaultTable();
+
+        Cursor c = mDatabase.query("test", null, "_id > 1000", null, null, null, null);
+        assertEquals(0, c.getCount());
+        assertTrue(c.isBeforeFirst());
+
+        try {
+            c.getInt(0);
+            fail("CursorIndexOutOfBoundsException expected");
+        } catch (CursorIndexOutOfBoundsException ex) {
+            // expected
+        }
+
+        int i;
+        for (c.moveToFirst(), i = 0; !c.isAfterLast(); c.moveToNext(), i++) {
+            c.getInt(0);
+        }
+        assertEquals(0, i);
+        try {
+            c.getInt(0);
+            fail("CursorIndexOutOfBoundsException expected");
+        } catch (CursorIndexOutOfBoundsException ex) {
+            // expected
+        }
+        c.close();
+    }
+
+    @MediumTest
+    public void testLargeField() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        StringBuilder sql = new StringBuilder(2100);
+        sql.append("INSERT INTO test (data) VALUES ('");
+        Random random = new Random(System.currentTimeMillis());
+        StringBuilder randomString = new StringBuilder(1979);
+        for (int i = 0; i < 1979; i++) {
+            randomString.append((random.nextInt() & 0xf) % 10);
+        }
+        sql.append(randomString);
+        sql.append("');");
+        mDatabase.execSQL(sql.toString());
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+
+        assertTrue(c.moveToFirst());
+        assertEquals(0, c.getPosition());
+        String largeString = c.getString(c.getColumnIndexOrThrow("data"));
+        assertNotNull(largeString);
+        assertEquals(randomString.toString(), largeString);
+        c.close();
+    }
+
+    class TestObserver extends DataSetObserver {
+        int total;
+        SQLiteCursor c;
+        boolean quit = false;
+        public TestObserver(int total_, SQLiteCursor cursor) {
+            c = cursor;
+            total = total_;
+        }
+        
+        @Override
+        public void onChanged() {
+            int count = c.getCount();
+            if (total == count) {
+                int i = 0;
+                while (c.moveToNext()) {
+                    assertEquals(i, c.getInt(1));
+                    i++;
+                }
+                assertEquals(count, i);
+                quit = true;
+                Looper.myLooper().quit();
+            }
+        }
+
+        @Override
+        public void onInvalidated() {
+        }
+    }
+    
+    //@Large
+    @Suppress
+    public void testLoadingThreadDelayRegisterData() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+        
+        final int count = 505; 
+        String sql = "INSERT INTO test (data) VALUES (?);";
+        SQLiteStatement s = mDatabase.compileStatement(sql);
+        for (int i = 0; i < count; i++) {
+            s.bindLong(1, i);
+            s.execute();
+        }
+
+        int maxRead = 500;
+        int initialRead = 5;
+        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+                null, initialRead, maxRead);
+        
+        TestObserver observer = new TestObserver(count, c);
+        c.getCount();
+        c.registerDataSetObserver(observer);
+        if (!observer.quit) {
+            Looper.loop();
+        }
+        c.close();
+    }
+    
+    @LargeTest
+    public void testLoadingThread() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+        
+        final int count = 50000; 
+        String sql = "INSERT INTO test (data) VALUES (?);";
+        SQLiteStatement s = mDatabase.compileStatement(sql);
+        for (int i = 0; i < count; i++) {
+            s.bindLong(1, i);
+            s.execute();
+        }
+
+        int maxRead = 1000;
+        int initialRead = 5;
+        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+                null, initialRead, maxRead);
+        
+        TestObserver observer = new TestObserver(count, c);
+        c.registerDataSetObserver(observer);
+        c.getCount();
+        
+        Looper.loop();
+        c.close();
+    } 
+    
+    @LargeTest
+    public void testLoadingThreadClose() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+        
+        final int count = 1000; 
+        String sql = "INSERT INTO test (data) VALUES (?);";
+        SQLiteStatement s = mDatabase.compileStatement(sql);
+        for (int i = 0; i < count; i++) {
+            s.bindLong(1, i);
+            s.execute();
+        }
+
+        int maxRead = 11;
+        int initialRead = 5;
+        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+                null, initialRead, maxRead);
+        
+        TestObserver observer = new TestObserver(count, c);
+        c.registerDataSetObserver(observer);
+        c.getCount();       
+        c.close();
+    }
+    
+    @LargeTest
+    public void testLoadingThreadDeactivate() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+        
+        final int count = 1000; 
+        String sql = "INSERT INTO test (data) VALUES (?);";
+        SQLiteStatement s = mDatabase.compileStatement(sql);
+        for (int i = 0; i < count; i++) {
+            s.bindLong(1, i);
+            s.execute();
+        }
+
+        int maxRead = 11;
+        int initialRead = 5;
+        SQLiteCursor c = (SQLiteCursor)mDatabase.rawQuery("select * from test;",
+                null, initialRead, maxRead);
+        
+        TestObserver observer = new TestObserver(count, c);
+        c.registerDataSetObserver(observer);
+        c.getCount();       
+        c.deactivate();
+        c.close();
+    }
+    
+    @LargeTest
+    public void testManyRowsLong() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");
+        
+        final int count = 36799; 
+        for (int i = 0; i < count; i++) {
+            mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
+        }
+
+        Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
+        assertNotNull(c);
+
+        int i = 0;
+        while (c.moveToNext()) {
+            assertEquals(i, c.getInt(0));
+            i++;
+        }
+        assertEquals(count, i);
+        assertEquals(count, c.getCount());
+
+        Log.d("testManyRows", "count " + Integer.toString(i));
+        c.close();
+    }
+
+    @LargeTest
+    public void testManyRowsTxt() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+        StringBuilder sql = new StringBuilder(2100);
+        sql.append("INSERT INTO test (data) VALUES ('");
+        Random random = new Random(System.currentTimeMillis());
+        StringBuilder randomString = new StringBuilder(1979);
+        for (int i = 0; i < 1979; i++) {
+            randomString.append((random.nextInt() & 0xf) % 10);
+        }
+        sql.append(randomString);
+        sql.append("');");
+
+        // if cursor window size changed, adjust this value too  
+        final int count = 600; // more than two fillWindow needed
+        for (int i = 0; i < count; i++) {
+            mDatabase.execSQL(sql.toString());
+        }
+
+        Cursor c = mDatabase.query("test", new String[]{"data"}, null, null, null, null, null);
+        assertNotNull(c);
+
+        int i = 0;
+        while (c.moveToNext()) {
+            assertEquals(randomString.toString(), c.getString(0));
+            i++;
+        }
+        assertEquals(count, i);
+        assertEquals(count, c.getCount());
+        c.close();
+    }
+    
+    @LargeTest
+    public void testManyRowsTxtLong() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, txt TEXT, data INT);");
+        
+        Random random = new Random(System.currentTimeMillis());
+        StringBuilder randomString = new StringBuilder(1979);
+        for (int i = 0; i < 1979; i++) {
+            randomString.append((random.nextInt() & 0xf) % 10);
+        }
+
+        // if cursor window size changed, adjust this value too  
+        final int count = 600;
+        for (int i = 0; i < count; i++) {
+            StringBuilder sql = new StringBuilder(2100);
+            sql.append("INSERT INTO test (txt, data) VALUES ('");
+            sql.append(randomString);
+            sql.append("','");
+            sql.append(i);
+            sql.append("');");
+            mDatabase.execSQL(sql.toString());
+        }
+
+        Cursor c = mDatabase.query("test", new String[]{"txt", "data"}, null, null, null, null, null);
+        assertNotNull(c);
+
+        int i = 0;
+        while (c.moveToNext()) {
+            assertEquals(randomString.toString(), c.getString(0));
+            assertEquals(i, c.getInt(1));
+            i++;
+        }
+        assertEquals(count, i);
+        assertEquals(count, c.getCount());
+        c.close();
+    }
+   
+    @MediumTest
+    public void testRequery() throws Exception {
+        populateDefaultTable();
+
+        Cursor c = mDatabase.rawQuery("SELECT * FROM test", null);
+        assertNotNull(c);
+        assertEquals(3, c.getCount());
+        c.deactivate();
+        c.requery();
+        assertEquals(3, c.getCount());
+        c.close();
+    }
+
+    @MediumTest
+    public void testRequeryWithSelection() throws Exception {
+        populateDefaultTable();
+
+        Cursor c = mDatabase.rawQuery("SELECT data FROM test WHERE data = '" + sString1 + "'",
+                null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals(sString1, c.getString(0));
+        c.deactivate();
+        c.requery();
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals(sString1, c.getString(0));
+        c.close();
+    }
+
+    @MediumTest
+    public void testRequeryWithSelectionArgs() throws Exception {
+        populateDefaultTable();
+
+        Cursor c = mDatabase.rawQuery("SELECT data FROM test WHERE data = ?",
+                new String[]{sString1});
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals(sString1, c.getString(0));
+        c.deactivate();
+        c.requery();
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals(sString1, c.getString(0));
+        c.close();
+    }
+
+    @MediumTest
+    public void testRequeryWithAlteredSelectionArgs() throws Exception {
+        /**
+         * Test the ability of a subclass of SQLiteCursor to change its query arguments.
+         */
+        populateDefaultTable();
+
+        SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
+            public Cursor newCursor(
+                    SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable,
+                    SQLiteQuery query) {
+                return new SQLiteCursor(db, masterQuery, editTable, query) {
+                    @Override
+                    public boolean requery() {
+                        setSelectionArguments(new String[]{"2"});
+                        return super.requery();
+                    }
+                };
+            }
+        };
+        Cursor c = mDatabase.rawQueryWithFactory(
+                factory, "SELECT data FROM test WHERE _id <= ?", new String[]{"1"},
+                null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals(sString1, c.getString(0));
+
+        // Our hacked requery() changes the query arguments in the cursor.
+        c.requery();
+
+        assertEquals(2, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals(sString1, c.getString(0));
+        assertTrue(c.moveToNext());
+        assertEquals(sString2, c.getString(0));
+
+        // Test that setting query args on a deactivated cursor also works.
+        c.deactivate();
+        c.requery();
+    }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
new file mode 100644
index 0000000..ca650e0
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.database;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.CharArrayBuffer;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.text.Collator;
+import java.util.Arrays;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_COLUMNNAME_INDEX;
+import static android.database.DatabaseUtils.InsertHelper.TABLE_INFO_PRAGMA_DEFAULT_INDEX;
+
+public class DatabaseGeneralTest extends AndroidTestCase implements PerformanceTestCase {
+
+    private static final String sString1 = "this is a test";
+    private static final String sString2 = "and yet another test";
+    private static final String sString3 = "this string is a little longer, but still a test";
+    private static final String PHONE_NUMBER = "16175551212";
+
+    private static final int CURRENT_DATABASE_VERSION = 42;
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        File dbDir = getContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE);
+        mDatabaseFile = new File(dbDir, "database_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    public boolean isPerformanceOnly() {
+        return false;
+    }
+
+    // These test can only be run once.
+    public int startPerformance(Intermediates intermediates) {
+        return 1;
+    }
+
+    private void populateDefaultTable() {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
+    }
+
+    @MediumTest
+    public void testVersion() throws Exception {
+        assertEquals(CURRENT_DATABASE_VERSION, mDatabase.getVersion());
+        mDatabase.setVersion(11);
+        assertEquals(11, mDatabase.getVersion());
+    }
+
+    @MediumTest
+    public void testUpdate() throws Exception {
+        populateDefaultTable();
+
+        ContentValues values = new ContentValues(1);
+        values.put("data", "this is an updated test");
+        assertEquals(1, mDatabase.update("test", values, "_id=1", null));
+        Cursor c = mDatabase.query("test", null, "_id=1", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        String value = c.getString(c.getColumnIndexOrThrow("data"));
+        assertEquals("this is an updated test", value);
+    }
+
+    @MediumTest
+    public void testPhoneNumbersEqual() throws Exception {
+        mDatabase.execSQL("CREATE TABLE phones (num TEXT);");
+        mDatabase.execSQL("INSERT INTO phones (num) VALUES ('911');");
+        mDatabase.execSQL("INSERT INTO phones (num) VALUES ('5555');");
+        mDatabase.execSQL("INSERT INTO phones (num) VALUES ('+" + PHONE_NUMBER + "');");
+
+        String number;
+        Cursor c;
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '504-555-7683')", null, null, null, null);
+        assertTrue(c == null || c.getCount() == 0);
+        c.close();
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '911')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("911", number);
+        c.close();
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '5555')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("5555", number);
+        c.close();
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '180055555555')", null, null, null, null);
+        assertTrue(c == null || c.getCount() == 0);
+        c.close();
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '+" + PHONE_NUMBER + "')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("+" + PHONE_NUMBER, number);
+        c.close();
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '+1 (617).555-1212')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("+" + PHONE_NUMBER, number);
+        c.close();
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '" + PHONE_NUMBER + "')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("+" + PHONE_NUMBER, number);
+        c.close();
+
+        /*
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '5551212')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("+" + PHONE_NUMBER, number);
+        c.close();
+        */
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '011" + PHONE_NUMBER + "')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("+" + PHONE_NUMBER, number);
+        c.close();
+
+        c = mDatabase.query("phones", null,
+                "PHONE_NUMBERS_EQUAL(num, '00" + PHONE_NUMBER + "')", null, null, null, null);
+        assertNotNull(c);
+        assertEquals(1, c.getCount());
+        c.moveToFirst();
+        number = c.getString(c.getColumnIndexOrThrow("num"));
+        assertEquals("+" + PHONE_NUMBER, number);
+        c.close();
+    }
+    
+    private void phoneNumberCompare(String phone1, String phone2, boolean equal, 
+            boolean useStrictComparation) {
+        String[] temporalPhoneNumbers = new String[2];
+        temporalPhoneNumbers[0] = phone1;
+        temporalPhoneNumbers[1] = phone2;
+
+        Cursor cursor = mDatabase.rawQuery(
+                String.format(
+                        "SELECT CASE WHEN PHONE_NUMBERS_EQUAL(?, ?, %d) " +
+                        "THEN 'equal' ELSE 'not equal' END",
+                        (useStrictComparation ? 1 : 0)),
+                temporalPhoneNumbers);
+        try {
+            assertNotNull(cursor);
+            assertTrue(cursor.moveToFirst());
+            if (equal) {
+                assertEquals(String.format("Unexpectedly, \"%s != %s\".", phone1, phone2),
+                        "equal", cursor.getString(0));
+            } else {
+                assertEquals(String.format("Unexpectedly, \"%s\" == \"%s\".", phone1, phone2),
+                        "not equal", cursor.getString(0));
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void assertPhoneNumberEqual(String phone1, String phone2) throws Exception {
+        assertPhoneNumberEqual(phone1, phone2, true);
+        assertPhoneNumberEqual(phone1, phone2, false);
+    }
+    
+    private void assertPhoneNumberEqual(String phone1, String phone2, boolean useStrict)
+            throws Exception {
+        phoneNumberCompare(phone1, phone2, true, useStrict);
+    }
+
+    private void assertPhoneNumberNotEqual(String phone1, String phone2) throws Exception {
+        assertPhoneNumberNotEqual(phone1, phone2, true);
+        assertPhoneNumberNotEqual(phone1, phone2, false);
+    }
+    
+    private void assertPhoneNumberNotEqual(String phone1, String phone2, boolean useStrict)
+            throws Exception {
+        phoneNumberCompare(phone1, phone2, false, useStrict);
+    }
+
+    /**
+     * Tests international matching issues for the PHONE_NUMBERS_EQUAL function.
+     * 
+     * @throws Exception
+     */
+    @SmallTest
+    public void testPhoneNumbersEqualInternationl() throws Exception {
+        assertPhoneNumberEqual("1", "1");
+        assertPhoneNumberEqual("123123", "123123");
+        assertPhoneNumberNotEqual("123123", "923123");
+        assertPhoneNumberNotEqual("123123", "123129");
+        assertPhoneNumberNotEqual("123123", "1231234");
+        assertPhoneNumberNotEqual("123123", "0123123", false);
+        assertPhoneNumberNotEqual("123123", "0123123", true);
+        assertPhoneNumberEqual("650-253-0000", "6502530000");
+        assertPhoneNumberEqual("650-253-0000", "650 253 0000");
+        assertPhoneNumberEqual("650 253 0000", "6502530000");
+        assertPhoneNumberEqual("+1 650-253-0000", "6502530000");
+        assertPhoneNumberEqual("001 650-253-0000", "6502530000");
+        assertPhoneNumberEqual("0111 650-253-0000", "6502530000");
+
+        // Russian trunk digit
+        assertPhoneNumberEqual("+79161234567", "89161234567");
+
+        // French trunk digit
+        assertPhoneNumberEqual("+33123456789", "0123456789");
+
+        // Trunk digit for city codes in the Netherlands
+        assertPhoneNumberEqual("+31771234567", "0771234567");
+
+        // Test broken caller ID seen on call from Thailand to the US
+        assertPhoneNumberEqual("+66811234567", "166811234567");
+
+        // Test the same in-country number with different country codes
+        assertPhoneNumberNotEqual("+33123456789", "+1123456789");
+
+        // Test one number with country code and the other without
+        assertPhoneNumberEqual("5125551212", "+15125551212");
+
+        // Test two NANP numbers that only differ in the area code
+        assertPhoneNumberNotEqual("5125551212", "6505551212");
+
+        // Japanese phone numbers
+        assertPhoneNumberEqual("090-1234-5678", "+819012345678");
+        assertPhoneNumberEqual("090(1234)5678", "+819012345678");
+        assertPhoneNumberEqual("090-1234-5678", "+81-90-1234-5678");
+
+        // Equador
+        assertPhoneNumberEqual("+593(800)123-1234", "8001231234");
+        assertPhoneNumberEqual("+593-2-1234-123", "21234123");
+
+        // Two continuous 0 at the beginning of the phone string should not be
+        // treated as trunk prefix in the strict comparation.
+        assertPhoneNumberEqual("008001231234", "8001231234", false);
+        assertPhoneNumberNotEqual("008001231234", "8001231234", true);
+
+        // Confirm that the bug found before does not re-appear in the strict compalation
+        assertPhoneNumberEqual("080-1234-5678", "+819012345678", false);
+        assertPhoneNumberNotEqual("080-1234-5678", "+819012345678", true);
+    }
+
+    @MediumTest
+    public void testCopyString() throws Exception {
+        mDatabase.execSQL("CREATE TABLE guess (numi INTEGER, numf FLOAT, str TEXT);");
+        mDatabase.execSQL(
+                "INSERT INTO guess (numi,numf,str) VALUES (0,0.0,'ZoomZoomZoomZoom');");
+        mDatabase.execSQL("INSERT INTO guess (numi,numf,str) VALUES (2000000000,3.1415926535,'');");
+        String chinese = "\u4eac\u4ec5 \u5c3d\u5f84\u60ca";
+        String[] arr = new String[1];
+        arr[0] = chinese;
+        mDatabase.execSQL("INSERT INTO guess (numi,numf,str) VALUES (-32768,-1.0,?)", arr);
+
+        Cursor c;
+
+        c = mDatabase.rawQuery("SELECT * FROM guess", null);
+        
+        c.moveToFirst();
+        
+        CharArrayBuffer buf = new CharArrayBuffer(14);
+        
+        String compareTo = c.getString(c.getColumnIndexOrThrow("numi"));
+        int numiIdx = c.getColumnIndexOrThrow("numi");
+        int numfIdx = c.getColumnIndexOrThrow("numf");
+        int strIdx = c.getColumnIndexOrThrow("str");
+        
+        c.copyStringToBuffer(numiIdx, buf);
+        assertEquals(1, buf.sizeCopied);
+        assertEquals(compareTo, new String(buf.data, 0, buf.sizeCopied));
+        
+        c.copyStringToBuffer(strIdx, buf);
+        assertEquals("ZoomZoomZoomZoom", new String(buf.data, 0, buf.sizeCopied));
+        
+        c.moveToNext();
+        compareTo = c.getString(numfIdx);
+        
+        c.copyStringToBuffer(numfIdx, buf);
+        assertEquals(compareTo, new String(buf.data, 0, buf.sizeCopied));
+        c.copyStringToBuffer(strIdx, buf);
+        assertEquals(0, buf.sizeCopied);
+        
+        c.moveToNext();
+        c.copyStringToBuffer(numfIdx, buf);
+        assertEquals(-1.0, Double.valueOf(
+                new String(buf.data, 0, buf.sizeCopied)).doubleValue());
+        
+        c.copyStringToBuffer(strIdx, buf);
+        compareTo = c.getString(strIdx);
+        assertEquals(chinese, compareTo);
+       
+        assertEquals(chinese, new String(buf.data, 0, buf.sizeCopied));
+        c.close();
+    }
+    
+    @MediumTest
+    public void testSchemaChange1() throws Exception {
+        SQLiteDatabase db1 = mDatabase;
+        Cursor cursor;
+
+        db1.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        cursor = db1.query("db1", null, null, null, null, null, null);
+        assertNotNull("Cursor is null", cursor);
+
+        db1.execSQL("CREATE TABLE db2 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        assertEquals(0, cursor.getCount());
+        cursor.deactivate();
+    }
+
+    @MediumTest
+    public void testSchemaChange2() throws Exception {
+        SQLiteDatabase db1 = mDatabase;
+        SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile, null);
+        Cursor cursor;
+
+        db1.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        cursor = db1.query("db1", null, null, null, null, null, null);
+        assertNotNull("Cursor is null", cursor);
+        assertEquals(0, cursor.getCount());
+        cursor.deactivate();
+        // this cause exception because we're still using sqlite_prepate16 and not
+        // sqlite_prepare16_v2. The v2 variant added the ability to check the
+        // schema version and handle the case when the schema has changed
+        // Marco Nelissen claim it was 2x slower to compile SQL statements so
+        // I reverted back to the v1 variant.
+        /* db2.execSQL("CREATE TABLE db2 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        cursor = db1.query("db1", null, null, null, null, null, null);
+        assertNotNull("Cursor is null", cursor);
+        assertEquals(0, cursor.count());
+        cursor.deactivate();
+        */
+    }
+
+    @MediumTest
+    public void testSchemaChange3() throws Exception {
+        SQLiteDatabase db1 = mDatabase;
+        SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile, null);
+        Cursor cursor;
+
+
+        db1.execSQL("CREATE TABLE db1 (_id INTEGER PRIMARY KEY, data TEXT);");
+        db1.execSQL("INSERT INTO db1 (data) VALUES ('test');");
+
+        cursor = db1.query("db1", null, null, null, null, null, null);
+        // this cause exception because we're still using sqlite_prepate16 and not
+        // sqlite_prepare16_v2. The v2 variant added the ability to check the
+        // schema version and handle the case when the schema has changed
+        // Marco Nelissen claim it was 2x slower to compile SQL statements so
+        // I reverted back to the v1 variant.
+        /* db2.execSQL("CREATE TABLE db2 (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        assertNotNull("Cursor is null", cursor);
+        assertEquals(1, cursor.count());
+        assertTrue(cursor.first());
+        assertEquals("test", cursor.getString(cursor.getColumnIndexOrThrow("data")));
+        cursor.deactivate();
+        */
+    }
+
+    private class ChangeObserver extends ContentObserver {
+        private int mCursorNotificationCount = 0;
+        private int mNotificationCount = 0;
+
+        public int getCursorNotificationCount() {
+            return mCursorNotificationCount;
+        }
+
+        public int getNotificationCount() {
+            return mNotificationCount;
+        }
+
+        public ChangeObserver(boolean cursor) {
+            super(new Handler());
+            mCursor = cursor;
+        }
+
+        @Override
+        public boolean deliverSelfNotifications() {
+            return true;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (mCursor) {
+                mCursorNotificationCount++;
+            } else {
+                mNotificationCount++;
+            }
+        }
+
+        boolean mCursor;
+    }
+
+    @MediumTest
+    public void testNotificationTest1() throws Exception {
+        /*
+        Cursor c = mContentResolver.query(Notes.CONTENT_URI,
+                new String[] {Notes._ID, Notes.NOTE},
+                null, null);
+        c.registerContentObserver(new MyContentObserver(true));
+        int count = c.count();
+
+        MyContentObserver observer = new MyContentObserver(false);
+        mContentResolver.registerContentObserver(Notes.CONTENT_URI, true, observer);
+
+        Uri uri;
+
+        HashMap<String, String> values = new HashMap<String, String>();
+        values.put(Notes.NOTE, "test note1");
+        uri = mContentResolver.insert(Notes.CONTENT_URI, values);
+        assertEquals(1, mCursorNotificationCount);
+        assertEquals(1, mNotificationCount);
+
+        c.requery();
+        assertEquals(count + 1, c.count());
+        c.first();
+        assertEquals("test note1", c.getString(c.getColumnIndex(Notes.NOTE)));
+        c.updateString(c.getColumnIndex(Notes.NOTE), "test note2");
+        c.commitUpdates();
+
+        assertEquals(2, mCursorNotificationCount);
+        assertEquals(2, mNotificationCount);
+
+        mContentResolver.delete(uri, null);
+
+        assertEquals(3, mCursorNotificationCount);
+        assertEquals(3, mNotificationCount);
+
+        mContentResolver.unregisterContentObserver(observer);
+        */
+    }
+
+    @MediumTest
+    public void testSelectionArgs() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+        ContentValues values = new ContentValues(1);
+        values.put("data", "don't forget to handled 's");
+        mDatabase.insert("test", "data", values);
+        values.clear();
+        values.put("data", "no apostrophes here");
+        mDatabase.insert("test", "data", values);
+        Cursor c = mDatabase.query(
+                "test", null, "data GLOB ?", new String[]{"*'*"}, null, null, null);
+        assertEquals(1, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals("don't forget to handled 's", c.getString(1));
+        c.deactivate();
+
+        // make sure code should checking null string properly so that
+        // it won't crash
+        try {
+            mDatabase.query("test", new String[]{"_id"},
+                    "_id=?", new String[]{null}, null, null, null);
+            fail("expected exception not thrown");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+    
+    @MediumTest
+    public void testTokenize() throws Exception {
+        Cursor c;
+        mDatabase.execSQL("CREATE TABLE tokens (" +
+                "token TEXT COLLATE unicode," +
+                "source INTEGER," +
+                "token_index INTEGER," +
+                "tag TEXT" +
+                ");");
+        mDatabase.execSQL("CREATE TABLE tokens_no_index (" +
+                "token TEXT COLLATE unicode," +
+                "source INTEGER" +
+                ");");
+        
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT _TOKENIZE(NULL, NULL, NULL, NULL)", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT _TOKENIZE('tokens', NULL, NULL, NULL)", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT _TOKENIZE('tokens', 10, NULL, NULL)", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT _TOKENIZE('tokens', 10, 'some string', NULL)", null));
+     
+        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT _TOKENIZE('tokens', 11, 'some string ok', ' ', 1, 'foo')", null));
+        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT _TOKENIZE('tokens', 11, 'second field', ' ', 1, 'bar')", null));
+
+        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT _TOKENIZE('tokens_no_index', 20, 'some string ok', ' ')", null));
+        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT _TOKENIZE('tokens_no_index', 21, 'foo bar baz', ' ', 0)", null));
+
+        // test Chinese
+        String chinese = new String("\u4eac\u4ec5 \u5c3d\u5f84\u60ca"); 
+        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT _TOKENIZE('tokens', 12,'" + chinese + "', ' ', 1)", null));
+        
+        String icustr = new String("Fr\u00e9d\u00e9ric Hj\u00f8nnev\u00e5g");
+        
+        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT _TOKENIZE('tokens', 13, '" + icustr + "', ' ', 1)", null));
+        
+        Assert.assertEquals(9, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT count(*) from tokens;", null));      
+
+        String key = DatabaseUtils.getHexCollationKey("Frederic Hjonneva");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));      
+        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        key = DatabaseUtils.getHexCollationKey("Hjonneva");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        
+        key = DatabaseUtils.getHexCollationKey("some string ok");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
+                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+        key = DatabaseUtils.getHexCollationKey("string");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
+                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+        key = DatabaseUtils.getHexCollationKey("ok");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
+                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+
+        key = DatabaseUtils.getHexCollationKey("second field");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
+                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+        key = DatabaseUtils.getHexCollationKey("field");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
+                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
+
+        key = DatabaseUtils.getHexCollationKey(chinese);
+        String[] a = new String[1];
+        a[0] = key;
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token= ?", a));
+        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token= ?", a));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token= ?", a));
+        a[0] += "*";
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+             "SELECT count(*) from tokens where token GLOB ?", a));        
+        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB ?", a));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB ?", a));
+
+       Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token= '" + key + "'", null));
+       Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+               "SELECT source from tokens where token= '" + key + "'", null));
+       Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+               "SELECT token_index from tokens where token= '" + key + "'", null));
+        
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));        
+        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        
+        key = DatabaseUtils.getHexCollationKey("\u4eac\u4ec5");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        
+        key = DatabaseUtils.getHexCollationKey("\u5c3d\u5f84\u60ca");
+        Log.d("DatabaseGeneralTest", "key = " + key);
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
+        
+        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
+                "SELECT count(*) from tokens where token GLOB 'ab*'", null));        
+
+        key = DatabaseUtils.getHexCollationKey("some string ok");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(20, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
+
+        key = DatabaseUtils.getHexCollationKey("bar");
+        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
+        Assert.assertEquals(21, DatabaseUtils.longForQuery(mDatabase,
+                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
+    }
+    
+    @MediumTest
+    public void testTransactions() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
+        mDatabase.execSQL("INSERT INTO test (num) VALUES (0)");
+
+        // Make sure that things work outside an explicit transaction.
+        setNum(1);
+        checkNum(1);
+
+        // Test a single-level transaction.
+        setNum(0);
+        mDatabase.beginTransaction();
+        setNum(1);
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        checkNum(1);
+        Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+        // Test a rolled-back transaction.
+        setNum(0);
+        mDatabase.beginTransaction();
+        setNum(1);
+        mDatabase.endTransaction();
+        checkNum(0);
+        Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+        // We should get an error if we end a non-existent transaction.
+        assertThrowsIllegalState(new Runnable() { public void run() {
+            mDatabase.endTransaction();
+        }});
+
+        // We should get an error if a set a non-existent transaction as clean.
+        assertThrowsIllegalState(new Runnable() { public void run() {
+            mDatabase.setTransactionSuccessful();
+        }});
+
+        mDatabase.beginTransaction();
+        mDatabase.setTransactionSuccessful();
+        // We should get an error if we mark a transaction as clean twice.
+        assertThrowsIllegalState(new Runnable() { public void run() {
+            mDatabase.setTransactionSuccessful();
+        }});
+        // We should get an error if we begin a transaction after marking the parent as clean.
+        assertThrowsIllegalState(new Runnable() { public void run() {
+            mDatabase.beginTransaction();
+        }});
+        mDatabase.endTransaction();
+        Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+        // Test a two-level transaction.
+        setNum(0);
+        mDatabase.beginTransaction();
+        mDatabase.beginTransaction();
+        setNum(1);
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        checkNum(1);
+        Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+        // Test rolling back an inner transaction.
+        setNum(0);
+        mDatabase.beginTransaction();
+        mDatabase.beginTransaction();
+        setNum(1);
+        mDatabase.endTransaction();
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        checkNum(0);
+        Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+
+        // Test rolling back an outer transaction.
+        setNum(0);
+        mDatabase.beginTransaction();
+        mDatabase.beginTransaction();
+        setNum(1);
+        mDatabase.setTransactionSuccessful();
+        mDatabase.endTransaction();
+        mDatabase.endTransaction();
+        checkNum(0);
+        Assert.assertFalse(mDatabase.isDbLockedByCurrentThread());
+    }
+
+    private void setNum(int num) {
+        mDatabase.execSQL("UPDATE test SET num = " + num);
+    }
+
+    private void checkNum(int num) {
+        Assert.assertEquals(
+                num, DatabaseUtils.longForQuery(mDatabase, "SELECT num FROM test", null));
+    }
+
+    private void assertThrowsIllegalState(Runnable r) {
+        boolean ok = false;
+        try {
+            r.run();
+        } catch (IllegalStateException e) {
+            ok = true;
+        }
+        Assert.assertTrue(ok);
+    }
+
+    // Disable these until we can explicitly mark them as stress tests
+    public void xxtestMem1() throws Exception {
+        populateDefaultTable();
+
+        for (int i = 0; i < 50000; i++) {
+            Cursor cursor = mDatabase.query("test", null, null, null, null, null, null);
+            cursor.moveToFirst();
+            cursor.close();
+//                Log.i("~~~~", "Finished round " + i);
+        }
+    }
+
+    // Disable these until we can explicitly mark them as stress tests
+    public void xxtestMem2() throws Exception {
+        populateDefaultTable();
+
+        for (int i = 0; i < 50000; i++) {
+            Cursor cursor = mDatabase.query("test", null, null, null, null, null, null);
+            cursor.close();
+//                Log.i("~~~~", "Finished round " + i);
+        }
+    }
+
+    // Disable these until we can explicitly mark them as stress tests
+    public void xxtestMem3() throws Exception {
+        populateDefaultTable();
+
+        for (int i = 0; i < 50000; i++) {
+            Cursor cursor = mDatabase.query("test", null, null, null, null, null, null);
+            cursor.deactivate();
+//                Log.i("~~~~", "Finished round " + i);
+        }
+    }
+
+    @MediumTest
+    public void testContentValues() throws Exception {
+        ContentValues values = new ContentValues();
+        values.put("string", "value");
+        assertEquals("value", values.getAsString("string"));
+        byte[] bytes = new byte[42];
+        Arrays.fill(bytes, (byte) 0x28);
+        values.put("byteArray", bytes);
+        assertTrue(Arrays.equals(bytes, values.getAsByteArray("byteArray")));
+
+        // Write the ContentValues to a Parcel and then read them out
+        Parcel p = Parcel.obtain();
+        values.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        values = ContentValues.CREATOR.createFromParcel(p);
+
+        // Read the values out again and make sure they're the same
+        assertTrue(Arrays.equals(bytes, values.getAsByteArray("byteArray")));
+        assertEquals("value", values.get("string"));
+    }
+
+    @MediumTest
+    public void testTableInfoPragma() throws Exception {
+        mDatabase.execSQL("CREATE TABLE pragma_test (" +
+                "i INTEGER DEFAULT 1234, " +
+                "j INTEGER, " +
+                "s TEXT DEFAULT 'hello', " +
+                "t TEXT, " +
+                "'select' TEXT DEFAULT \"hello\")");
+        try {
+            Cursor cur = mDatabase.rawQuery("PRAGMA table_info(pragma_test)", null);
+            Assert.assertEquals(5, cur.getCount());
+
+            Assert.assertTrue(cur.moveToNext());
+            Assert.assertEquals("i",
+                    cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+            Assert.assertEquals("1234",
+                    cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+            Assert.assertTrue(cur.moveToNext());
+            Assert.assertEquals("j",
+                    cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+            Assert.assertNull(cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+            Assert.assertTrue(cur.moveToNext());
+            Assert.assertEquals("s",
+                    cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+            Assert.assertEquals("'hello'",
+                    cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+            Assert.assertTrue(cur.moveToNext());
+            Assert.assertEquals("t",
+                    cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+            Assert.assertNull(cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+            Assert.assertTrue(cur.moveToNext());
+            Assert.assertEquals("select",
+                    cur.getString(TABLE_INFO_PRAGMA_COLUMNNAME_INDEX));
+            Assert.assertEquals("\"hello\"",
+                    cur.getString(TABLE_INFO_PRAGMA_DEFAULT_INDEX));
+
+            cur.close();
+        } catch (Throwable t) {
+            throw new RuntimeException(
+                    "If you see this test fail, it's likely that something about " +
+                    "sqlite's PRAGMA table_info(...) command has changed.", t);
+        }
+    }
+
+    @MediumTest
+    public void testInsertHelper() throws Exception {
+        Cursor cur;
+        ContentValues cv;
+        long row;
+
+        mDatabase.execSQL("CREATE TABLE insert_test (" +
+                "_id INTEGER PRIMARY KEY, " +
+                "s TEXT NOT NULL UNIQUE, " +
+                "t TEXT NOT NULL DEFAULT 'hello world', " +
+                "i INTEGER, " +
+                "j INTEGER NOT NULL DEFAULT 1234, " +
+                "'select' TEXT)");
+
+        DatabaseUtils.InsertHelper ih =
+            new DatabaseUtils.InsertHelper(mDatabase, "insert_test");
+
+        cv = new ContentValues();
+        cv.put("s", "one");
+        row = ih.insert(cv);
+        cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+        Assert.assertTrue(cur.moveToFirst());
+        Assert.assertEquals("one", cur.getString(1));
+        Assert.assertEquals("hello world", cur.getString(2));
+        Assert.assertNull(cur.getString(3));
+        Assert.assertEquals(1234, cur.getLong(4));
+        Assert.assertNull(cur.getString(5));
+
+        cv = new ContentValues();
+        cv.put("s", "two");
+        cv.put("t", "goodbye world");
+        row = ih.insert(cv);
+        cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+        Assert.assertTrue(cur.moveToFirst());
+        Assert.assertEquals("two", cur.getString(1));
+        Assert.assertEquals("goodbye world", cur.getString(2));
+        Assert.assertNull(cur.getString(3));
+        Assert.assertEquals(1234, cur.getLong(4));
+        Assert.assertNull(cur.getString(5));
+
+        cv = new ContentValues();
+        cv.put("t", "goodbye world");
+        row = ih.insert(cv);
+        Assert.assertEquals(-1, row);
+
+        cv = new ContentValues();
+        cv.put("s", "three");
+        cv.put("i", 2345);
+        cv.put("j", 3456);
+        cv.put("select", "tricky");
+        row = ih.insert(cv);
+        cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+        Assert.assertTrue(cur.moveToFirst());
+        Assert.assertEquals("three", cur.getString(1));
+        Assert.assertEquals("hello world", cur.getString(2));
+        Assert.assertEquals(2345, cur.getLong(3));
+        Assert.assertEquals(3456, cur.getLong(4));
+        Assert.assertEquals("tricky", cur.getString(5));
+
+        cv = new ContentValues();
+        cv.put("s", "three");
+        cv.put("i", 6789);
+        row = ih.insert(cv);
+        Assert.assertEquals(-1, row);
+        row = ih.replace(cv);
+        cur = mDatabase.rawQuery("SELECT * FROM insert_test WHERE _id == " + row, null);
+        Assert.assertTrue(cur.moveToFirst());
+        Assert.assertEquals("three", cur.getString(1));
+        Assert.assertEquals("hello world", cur.getString(2));
+        Assert.assertEquals(6789, cur.getLong(3));
+
+        ih.close();
+    }
+
+    @MediumTest
+    public void testDbCloseReleasingAllCachedSql() {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, text1 TEXT, text2 TEXT, " +
+                "num1 INTEGER, num2 INTEGER, image BLOB);");
+        final String statement = "DELETE FROM test WHERE _id=?;";
+        SQLiteStatement statementDoNotClose = mDatabase.compileStatement(statement);
+        assertTrue(statementDoNotClose.getUniqueId() > 0);
+        int nStatement = statementDoNotClose.getUniqueId();
+        assertTrue(statementDoNotClose.getUniqueId() == nStatement);
+        /* do not close statementDoNotClose object. 
+         * That should leave it in SQLiteDatabase.mPrograms.
+         * mDatabase.close() in tearDown() should release it.
+         */
+    }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseLocaleTest.java b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
new file mode 100644
index 0000000..b3282941
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseLocaleTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+package android.database;
+
+import android.database.sqlite.SQLiteDatabase;
+import android.database.Cursor;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import android.test.MoreAsserts;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+public class DatabaseLocaleTest extends TestCase {
+
+    private SQLiteDatabase mDatabase;
+
+    private static final String[] STRINGS = {
+        "c\u00f4t\u00e9",
+        "cote",
+        "c\u00f4te",
+        "cot\u00e9",
+        "boy",
+        "dog",
+        "COTE",
+    };
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDatabase = SQLiteDatabase.create(null);
+        mDatabase.execSQL(
+                "CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT COLLATE LOCALIZED);");
+    }
+
+    private void insertStrings() {
+        for (String s : STRINGS) {
+            mDatabase.execSQL("INSERT INTO test (data) VALUES('" + s + "');");
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        super.tearDown();
+    }
+
+    private String[] query(String sql) {
+        Log.i("LocaleTest", "Querying: " + sql);
+        Cursor c = mDatabase.rawQuery(sql, null);
+        assertNotNull(c);
+        ArrayList<String> items = new ArrayList<String>();
+        while (c.moveToNext()) {
+            items.add(c.getString(0));
+            Log.i("LocaleTest", "...." + c.getString(0));
+        }
+        String[] result = items.toArray(new String[items.size()]);
+        assertEquals(STRINGS.length, result.length);
+        c.close();
+        return result;
+    }
+
+    @MediumTest
+    public void testLocaleInsertOrder() throws Exception {
+        insertStrings();
+        String[] results = query("SELECT data FROM test");
+        MoreAsserts.assertEquals(STRINGS, results);
+    }
+
+    @MediumTest
+    public void testLocaleenUS() throws Exception {
+        insertStrings();
+        Log.i("LocaleTest", "about to call setLocale en_US");
+        mDatabase.setLocale(new Locale("en", "US"));
+        String[] results;
+        results = query("SELECT data FROM test ORDER BY data COLLATE LOCALIZED ASC");
+
+        // The database code currently uses PRIMARY collation strength,
+        // meaning that all versions of a character compare equal (regardless
+        // of case or accents), leaving the "cote" flavors in database order.
+        MoreAsserts.assertEquals(results, new String[] {
+                STRINGS[4],  // "boy"
+                STRINGS[0],  // sundry forms of "cote"
+                STRINGS[1],
+                STRINGS[2],
+                STRINGS[3],
+                STRINGS[6],  // "COTE"
+                STRINGS[5],  // "dog"
+        });
+    }
+
+    @SmallTest
+    public void testHoge() throws Exception {
+        Cursor cursor = null;
+        try {
+            String expectedString = new String(new int[] {0xFE000}, 0, 1);
+            mDatabase.execSQL("INSERT INTO test(id, data) VALUES(1, '" + expectedString + "')");
+            cursor = mDatabase.rawQuery("SELECT data FROM test WHERE id = 1", null);
+            
+            assertNotNull(cursor);
+            assertTrue(cursor.moveToFirst());
+            String actualString = cursor.getString(0);
+            assertEquals(expectedString.length(), actualString.length());
+            for (int i = 0; i < expectedString.length(); i++) {
+                assertEquals((int)expectedString.charAt(i), (int)actualString.charAt(i));
+            }
+            assertEquals(expectedString, actualString);
+        } finally {
+            if (cursor != null) cursor.close();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseLockTest.java b/core/tests/coretests/src/android/database/DatabaseLockTest.java
new file mode 100644
index 0000000..f7a9f8a
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseLockTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.database;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+import java.io.File;
+import java.util.concurrent.atomic.AtomicInteger;
+import android.test.AndroidTestCase;
+
+import junit.framework.TestCase;
+
+/* 
+ * This is a series of unit tests for database locks.
+ *
+ * Suppress these tests for now, since they have has inconsistent results.
+ * This should be turned into a performance tracking test.
+ */
+@Suppress
+public class DatabaseLockTest extends AndroidTestCase {
+
+    private static final int NUM_ITERATIONS = 100;
+    private static final int SLEEP_TIME = 30;
+    private static final int MAX_ALLOWED_LATENCY_TIME = 30;
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private AtomicInteger mCounter = new AtomicInteger();
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        File parentDir = getContext().getFilesDir();
+        mDatabaseFile = new File(parentDir, "database_test.db");
+        
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    /*
+     * testLockFairness() tests the fairness of prioritizing multiple threads 
+     * attempting to access a database concurrently.
+     * This test is intended to verify that, when two threads are accessing the
+     * same database at the same time with the same prioritization, neither thread 
+     * is locked out and prevented from accessing the database.
+     */
+    @Suppress
+    public void testLockFairness() {
+        startDatabaseFairnessThread();
+        int previous = 0;
+        for (int i = 0; i < NUM_ITERATIONS; i++) { 
+            mDatabase.beginTransaction();
+            int val = mCounter.get();
+            if (i == 0) {
+                previous = val - i;
+            }
+            assertTrue(previous == (val - i));
+            try {
+                Thread.currentThread().sleep(SLEEP_TIME); 
+            } catch (InterruptedException e) {
+                // ignore
+            }
+            mDatabase.endTransaction();
+        }
+    }
+    
+    /*
+     * This function is to create the second thread for testLockFairness() test.
+     */
+    private void startDatabaseFairnessThread() {
+        Thread thread = new DatabaseFairnessThread();
+        thread.start();
+    }
+
+    private class DatabaseFairnessThread extends Thread {
+        @Override
+        public void run() {
+            for (int i = 0; i < NUM_ITERATIONS; i++) {
+                mDatabase.beginTransaction();
+                int val = mCounter.incrementAndGet();
+                try {
+                    Thread.currentThread().sleep(SLEEP_TIME);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+                mDatabase.endTransaction();
+            }
+        }
+    }    
+    
+    /*
+     * testLockLatency() tests the latency of database locks.
+     * This test is intended to verify that, even when two threads are accessing
+     * the same database, the locking/unlocking of the database is done within an
+     * appropriate amount of time (MAX_ALLOWED_LATENCY_TIME).
+     */
+    @Suppress
+    public void testLockLatency() {
+        startDatabaseLatencyThread();
+        int previous = 0;
+        long sumTime = 0;
+        long maxTime = 0;
+        for (int i = 0; i < NUM_ITERATIONS; i++) { 
+            long startTime = System.currentTimeMillis();
+            mDatabase.beginTransaction();
+            long endTime = System.currentTimeMillis();
+            long elapsedTime = endTime - startTime;
+            if (maxTime < elapsedTime) {
+                maxTime = elapsedTime;
+            }
+            sumTime += elapsedTime;
+            try {
+                Thread.currentThread().sleep(SLEEP_TIME); 
+            } catch (InterruptedException e) {
+                // ignore
+            }   
+            mDatabase.endTransaction();
+        }
+        long averageTime = sumTime/NUM_ITERATIONS;
+        Log.i("DatabaseLockLatency", "AverageTime: " + averageTime);
+        Log.i("DatabaseLockLatency", "MaxTime: " + maxTime);
+        assertTrue( (averageTime - SLEEP_TIME) <= MAX_ALLOWED_LATENCY_TIME);
+    }
+    
+    /*
+     * This function is to create the second thread for testLockLatency() test.
+     */
+    private void startDatabaseLatencyThread() {
+        Thread thread = new DatabaseLatencyThread();
+        thread.start();
+    }
+
+    private class DatabaseLatencyThread extends Thread {
+        @Override
+        public void run() {
+            for (int i = 0; i < NUM_ITERATIONS; i++) 
+            {
+                mDatabase.beginTransaction();
+                try {
+                    Thread.currentThread().sleep(SLEEP_TIME);
+                } catch (InterruptedException e) {
+                    // ignore
+                } 
+                mDatabase.endTransaction();
+            }
+        }
+    }        
+}
diff --git a/core/tests/coretests/src/android/database/DatabasePerformanceTests.java b/core/tests/coretests/src/android/database/DatabasePerformanceTests.java
new file mode 100644
index 0000000..b8ebcc4
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabasePerformanceTests.java
@@ -0,0 +1,1353 @@
+/*
+ * 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.
+ */
+
+package android.database;
+
+import junit.framework.Assert;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.provider.Contacts;
+import android.provider.Contacts.People;
+import android.test.PerformanceTestCase;
+import android.test.TestCase;
+
+import java.io.File;
+import java.util.Random;
+
+/**
+ * Database Performance Tests
+ * 
+ */
+
+public class DatabasePerformanceTests {
+
+    public static String[] children() {
+        return new String[] {
+            ContactReadingTest1.class.getName(),
+            Perf1Test.class.getName(),
+            Perf2Test.class.getName(),
+            Perf3Test.class.getName(),
+            Perf4Test.class.getName(),
+            Perf5Test.class.getName(),
+            Perf6Test.class.getName(),
+            Perf7Test.class.getName(),
+            Perf8Test.class.getName(),
+            Perf9Test.class.getName(),
+            Perf10Test.class.getName(),
+            Perf11Test.class.getName(),
+            Perf12Test.class.getName(),
+            Perf13Test.class.getName(),
+            Perf14Test.class.getName(),
+            Perf15Test.class.getName(),
+            Perf16Test.class.getName(),
+            Perf17Test.class.getName(),
+            Perf18Test.class.getName(),
+            Perf19Test.class.getName(),
+            Perf20Test.class.getName(),
+            Perf21Test.class.getName(),
+            Perf22Test.class.getName(),
+            Perf23Test.class.getName(),
+            Perf24Test.class.getName(),
+            Perf25Test.class.getName(),
+            Perf26Test.class.getName(),
+            Perf27Test.class.getName(),
+            Perf28Test.class.getName(),
+            Perf29Test.class.getName(),
+            Perf30Test.class.getName(),
+            Perf31Test.class.getName(),
+            };
+    }
+       
+    public static abstract class PerformanceBase implements TestCase,
+            PerformanceTestCase {
+        protected static final int CURRENT_DATABASE_VERSION = 42;
+        protected SQLiteDatabase mDatabase;
+        protected File mDatabaseFile;
+        protected Context mContext;
+
+        public void setUp(Context c) {
+            mContext = c;
+            mDatabaseFile = new File("/tmp", "perf_database_test.db");
+            if (mDatabaseFile.exists()) {
+                mDatabaseFile.delete();
+            }
+            mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+            Assert.assertTrue(mDatabase != null);
+            mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+        }
+
+        public void tearDown() {
+            mDatabase.close();
+            mDatabaseFile.delete();
+        }
+
+        public boolean isPerformanceOnly() {
+            return true;
+        }
+
+        // These test can only be run once.
+        public int startPerformance(Intermediates intermediates) {
+            return 0;
+        }
+
+        public void run() {
+        }
+
+        public String numberName(int number) {
+            String result = "";
+
+            if (number >= 1000) {
+                result += numberName((number / 1000)) + " thousand";
+                number = (number % 1000);
+
+                if (number > 0) result += " ";
+            }
+
+            if (number >= 100) {
+                result += ONES[(number / 100)] + " hundred";
+                number = (number % 100);
+
+                if (number > 0) result += " ";
+            }
+
+            if (number >= 20) {
+                result += TENS[(number / 10)];
+                number = (number % 10);
+
+                if (number > 0) result += " ";
+            }
+
+            if (number > 0) {
+                result += ONES[number];
+            }
+
+            return result;
+        }
+    }
+
+    /**
+     * Test reading all contact data.
+     */
+    public static class ContactReadingTest1 implements TestCase, PerformanceTestCase {
+        private static final String[] PEOPLE_PROJECTION = new String[] {
+               Contacts.People._ID, // 0
+               Contacts.People.PRIMARY_PHONE_ID, // 1
+               Contacts.People.TYPE, // 2
+               Contacts.People.NUMBER, // 3
+               Contacts.People.LABEL, // 4
+               Contacts.People.NAME, // 5
+               Contacts.People.PRESENCE_STATUS, // 6
+        };
+
+        private Cursor mCursor;
+
+        public void setUp(Context c) {
+            mCursor = c.getContentResolver().query(People.CONTENT_URI, PEOPLE_PROJECTION, null,
+                    null, People.DEFAULT_SORT_ORDER);
+        }
+        
+        public void tearDown() {
+            mCursor.close();
+        }
+
+        public boolean isPerformanceOnly() {
+            return true;
+        }
+
+        public int startPerformance(Intermediates intermediates) {
+            // This test can only be run once.
+            return 0;
+        }
+
+        public void run() {
+            while (mCursor.moveToNext()) {
+                // Read out all of the data
+                mCursor.getLong(0);
+                mCursor.getLong(1);
+                mCursor.getLong(2);
+                mCursor.getString(3);
+                mCursor.getString(4);
+                mCursor.getString(5);
+                mCursor.getLong(6);
+            }
+        }
+    }
+    
+    /**
+     * Test 1000 inserts
+     */
+    
+    public static class Perf1Test extends PerformanceBase {
+        private static final int SIZE = 1000;
+
+        private String[] statements = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                statements[i] =
+                        "INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                                + numberName(r) + "')";
+            }
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.execSQL(statements[i]);
+            }
+        }
+    }
+
+    /**
+     * Test 1000 inserts into and indexed table
+     */
+    
+    public static class Perf2Test extends PerformanceBase {
+        private static final int SIZE = 1000;
+
+        private String[] statements = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                statements[i] =
+                        "INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                                + numberName(r) + "')";
+            }
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1c ON t1(c)");
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.execSQL(statements[i]);
+            }
+        }
+    }
+
+    /**
+     * 100 SELECTs without an index
+     */
+      
+    public static class Perf3Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "b >= " + lower + " AND b < " + upper;
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase
+                        .query("t1", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     * 100 SELECTs on a string comparison
+     */
+    
+    public static class Perf4Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                where[i] = "c LIKE '" + numberName(i) + "'";
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase
+                        .query("t1", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     * 100 SELECTs with an index
+     */
+    
+    public static class Perf5Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "b >= " + lower + " AND b < " + upper;
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase
+                        .query("t1", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  INNER JOIN without an index
+     */
+    
+    public static class Perf6Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"t1.a"};
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase
+              .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+        }
+
+        @Override
+        public void run() {
+            mDatabase.query("t1 INNER JOIN t2 ON t1.b = t2.b", COLUMNS, null,
+                    null, null, null, null);
+        }
+    }
+
+    /**
+     *  INNER JOIN without an index on one side
+     */
+    
+    public static class Perf7Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"t1.a"};
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase
+              .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+        }
+
+        @Override
+        public void run() {
+            mDatabase.query("t1 INNER JOIN t2 ON t1.b = t2.b", COLUMNS, null,
+                    null, null, null, null);
+        }
+    }
+
+    /**
+     *  INNER JOIN without an index on one side
+     */
+    
+    public static class Perf8Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"t1.a"};
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase
+              .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+        }
+
+        @Override
+        public void run() {
+            mDatabase.query("t1 INNER JOIN t2 ON t1.c = t2.c", COLUMNS, null,
+                    null, null, null, null);
+        }
+    }
+
+    /**
+     *  100 SELECTs with subqueries. Subquery is using an index
+     */
+    
+    public static class Perf9Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"t1.a"};
+
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase
+              .execSQL("CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            mDatabase.execSQL("CREATE INDEX i2b ON t2(b)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t2 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] =
+                        "t1.b IN (SELECT t2.b FROM t2 WHERE t2.b >= " + lower
+                                + " AND t2.b < " + upper + ")";
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase
+                        .query("t1", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  100 SELECTs on string comparison with Index
+     */
+
+    public static class Perf10Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"count(*)", "avg(b)"};
+
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i3c ON t1(c)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                where[i] = "c LIKE '" + numberName(i) + "'";
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase
+                        .query("t1", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  100 SELECTs on integer 
+     */
+    
+    public static class Perf11Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"b"};
+
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  100 SELECTs on String
+     */
+
+    public static class Perf12Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"c"};
+
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  100 SELECTs on integer with index
+     */
+    
+    public static class Perf13Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"b"};
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1b on t1(b)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  100 SELECTs on String with index
+     */
+
+    public static class Perf14Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"c"};      
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1c ON t1(c)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t1", COLUMNS, null, null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  100 SELECTs on String with starts with
+     */
+
+    public static class Perf15Test extends PerformanceBase {
+        private static final int SIZE = 100;
+        private static final String[] COLUMNS = {"c"};
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1c ON t1(c)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                where[i] = "c LIKE '" + numberName(r).substring(0, 1) + "*'";
+
+            }
+
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase
+                        .query("t1", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+
+    /**
+     *  1000  Deletes on an indexed table
+     */
+    
+    public static class Perf16Test extends PerformanceBase {
+        private static final int SIZE = 1000;
+        private static final String[] COLUMNS = {"c"};
+        
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i3c ON t1(c)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.delete("t1", null, null);
+            }
+        }
+    }
+
+    /**
+     *  1000  Deletes
+     */
+    
+    public static class Perf17Test extends PerformanceBase {
+        private static final int SIZE = 1000;
+        private static final String[] COLUMNS = {"c"};       
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.delete("t1", null, null);
+            }
+        }
+    }
+
+    /**
+     *  1000 DELETE's without an index with where clause 
+     */
+    
+    public static class Perf18Test extends PerformanceBase {
+        private static final int SIZE = 1000;
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "b >= " + lower + " AND b < " + upper;
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.delete("t1", where[i], null);
+            }
+        }
+    }
+
+    /**
+     *  1000 DELETE's with an index with where clause 
+     */
+    
+    public static class Perf19Test extends PerformanceBase {
+        private static final int SIZE = 1000;
+        private String[] where = new String[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "b >= " + lower + " AND b < " + upper;
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.delete("t1", where[i], null);
+            }
+        }
+    }
+
+    /**
+     *  1000 update's with an index with where clause 
+     */
+    
+    public static class Perf20Test extends PerformanceBase {
+        private static final int SIZE = 1000;
+        private String[] where = new String[SIZE];
+        ContentValues[] mValues = new ContentValues[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1b ON t1(b)");
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "b >= " + lower + " AND b < " + upper;
+                ContentValues b = new ContentValues(1);
+                b.put("b", upper);
+                mValues[i] = b;
+               
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.update("t1", mValues[i], where[i], null);
+            }
+        }
+    }
+
+    /**
+     *  1000 update's without an index with where clause 
+     */
+    
+    public static class Perf21Test extends PerformanceBase {
+        private static final int SIZE = 1000;       
+        private String[] where = new String[SIZE];
+        ContentValues[] mValues = new ContentValues[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100))");
+           
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t1 VALUES(" + i + "," + r + ",'"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "b >= " + lower + " AND b < " + upper;
+                ContentValues b = new ContentValues(1);
+                b.put("b", upper);
+                mValues[i] = b;
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.update("t1", mValues[i], where[i], null);
+            }
+        }
+    }
+    
+    /**
+     *  10000 inserts for an integer 
+     */
+    
+    public static class Perf22Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        ContentValues[] mValues = new ContentValues[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER)");
+           
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                ContentValues b = new ContentValues(1);
+                b.put("a", r);
+                mValues[i] = b;
+            }
+        }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.insert("t1", null, mValues[i]);
+            }
+        }
+    }
+    
+    /**
+     *  10000 inserts for an integer -indexed table
+     */
+    
+    public static class Perf23Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        ContentValues[] mValues = new ContentValues[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a INTEGER)");
+            mDatabase.execSQL("CREATE INDEX i1a ON t1(a)");
+           
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                ContentValues b = new ContentValues(1);
+                b.put("a", r);
+                mValues[i] = b;
+            }
+        }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.insert("t1", null, mValues[i]);
+            }
+        }
+    }
+    
+    /**
+     *  10000 inserts for a String 
+     */
+    
+    public static class Perf24Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        ContentValues[] mValues = new ContentValues[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a VARCHAR(100))");
+           
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                ContentValues b = new ContentValues(1);
+                b.put("a", numberName(r));
+                mValues[i] = b;
+            }
+        }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.insert("t1", null, mValues[i]);
+            }
+        }
+    }
+    
+    /**
+     *  10000 inserts for a String - indexed table 
+     */
+    
+    public static class Perf25Test extends PerformanceBase {
+        private static final int SIZE = 10000;       
+        ContentValues[] mValues = new ContentValues[SIZE];
+
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t1(a VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i1a ON t1(a)");
+                       
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                ContentValues b = new ContentValues(1);
+                b.put("a", numberName(r));
+                mValues[i] = b; 
+            }
+        }
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.insert("t1", null, mValues[i]);
+            }
+        }
+    }
+    
+    
+    /**
+     *  10000 selects for a String -starts with
+     */
+    
+    public static class Perf26Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        private static final String[] COLUMNS = {"t3.a"};
+        private String[] where = new String[SIZE];
+        
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+                                  
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t3 VALUES('"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                where[i] = "a LIKE '" + numberName(r).substring(0, 1) + "*'";
+
+            }
+        }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+    
+    /**
+     *  10000 selects for a String - indexed table -starts with
+     */
+    
+    public static class Perf27Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        private static final String[] COLUMNS = {"t3.a"};
+        private String[] where = new String[SIZE];
+        
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i3a ON t3(a)");
+                       
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t3 VALUES('"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                where[i] = "a LIKE '" + numberName(r).substring(0, 1) + "*'";
+
+            }                              
+           }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+    
+    /**
+     *  10000 selects for an integer -
+     */
+    
+    public static class Perf28Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        private static final String[] COLUMNS = {"t4.a"};
+        private String[] where = new String[SIZE];
+        
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t4(a INTEGER)");
+           
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t4 VALUES(" + r + ")");
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "a >= " + lower + " AND a < " + upper;
+            }
+           }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t4", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+    
+    /**
+     *  10000 selects for an integer -indexed table
+     */
+    
+    public static class Perf29Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        private static final String[] COLUMNS = {"t4.a"};
+        private String[] where = new String[SIZE];
+       
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t4(a INTEGER)");
+           mDatabase.execSQL("CREATE INDEX i4a ON t4(a)");
+           
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t4 VALUES(" + r + ")");
+                
+                int lower = i * 100;
+                int upper = (i + 10) * 100;
+                where[i] = "a >= " + lower + " AND a < " + upper;
+            }
+           
+           }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t4", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+    
+    
+    /**
+     *  10000 selects for a String - contains 'e'
+     */
+    
+    public static class Perf30Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        private static final String[] COLUMNS = {"t3.a"};
+        private String[] where = new String[SIZE];
+        
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+            
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t3 VALUES('"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                 where[i] = "a LIKE '*e*'";
+
+            }                              
+           }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+    
+    /**
+     *  10000 selects for a String - contains 'e'-indexed table
+     */
+    
+    public static class Perf31Test extends PerformanceBase {
+        private static final int SIZE = 10000;
+        private static final String[] COLUMNS = {"t3.a"};
+        private String[] where = new String[SIZE];
+        
+        @Override
+        public void setUp(Context c) {
+            super.setUp(c);
+            Random random = new Random(42);
+
+            mDatabase
+              .execSQL("CREATE TABLE t3(a VARCHAR(100))");
+            mDatabase.execSQL("CREATE INDEX i3a ON t3(a)");
+            
+            for (int i = 0; i < SIZE; i++) {
+                int r = random.nextInt(100000);
+                mDatabase.execSQL("INSERT INTO t3 VALUES('"
+                        + numberName(r) + "')");
+            }
+
+            for (int i = 0; i < SIZE; i++) {
+                where[i] = "a LIKE '*e*'";
+
+            }                              
+            
+           }        
+
+        @Override
+        public void run() {
+            for (int i = 0; i < SIZE; i++) {
+                mDatabase.query("t3", COLUMNS, where[i], null, null, null, null);
+            }
+        }
+    }
+    
+    public static final String[] ONES =
+            {"zero", "one", "two", "three", "four", "five", "six", "seven",
+                "eight", "nine", "ten", "eleven", "twelve", "thirteen",
+                "fourteen", "fifteen", "sixteen", "seventeen", "eighteen",
+                "nineteen"};
+
+    public static final String[] TENS =
+            {"", "ten", "twenty", "thirty", "forty", "fifty", "sixty",
+                "seventy", "eighty", "ninety"};
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseStatementTest.java b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
new file mode 100644
index 0000000..71dc3ae
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseStatementTest.java
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+package android.database;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteConstraintException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDoneException;
+import android.database.sqlite.SQLiteStatement;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.io.File;
+
+public class DatabaseStatementTest extends AndroidTestCase implements PerformanceTestCase {
+
+    private static final String sString1 = "this is a test";
+    private static final String sString2 = "and yet another test";
+    private static final String sString3 = "this string is a little longer, but still a test";
+    
+    private static final int CURRENT_DATABASE_VERSION = 42;
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+	File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
+	mDatabaseFile = new File(dbDir, "database_test.db");
+
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    public boolean isPerformanceOnly() {
+        return false;
+    }
+
+    // These test can only be run once.
+    public int startPerformance(Intermediates intermediates) {
+        return 1;
+    }
+
+    private void populateDefaultTable() {
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
+        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
+    }
+
+    @MediumTest
+    public void testExecuteStatement() throws Exception {
+        populateDefaultTable();
+        SQLiteStatement statement = mDatabase.compileStatement("DELETE FROM test");
+        statement.execute();
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+        assertEquals(0, c.getCount());
+        c.deactivate();
+        statement.close();
+    }
+
+    @MediumTest
+    public void testSimpleQuery() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num INTEGER NOT NULL, str TEXT NOT NULL);");
+        mDatabase.execSQL("INSERT INTO test VALUES (1234, 'hello');");
+        SQLiteStatement statement1 =
+                mDatabase.compileStatement("SELECT num FROM test WHERE str = ?");
+        SQLiteStatement statement2 =
+                mDatabase.compileStatement("SELECT str FROM test WHERE num = ?");
+
+        try {
+            statement1.bindString(1, "hello");
+            long value = statement1.simpleQueryForLong();
+            assertEquals(1234, value);
+
+            statement1.bindString(1, "world");
+            statement1.simpleQueryForLong();
+            fail("shouldn't get here");
+        } catch (SQLiteDoneException e) {
+            // expected
+        }
+
+        try {
+            statement2.bindLong(1, 1234);
+            String value = statement1.simpleQueryForString();
+            assertEquals("hello", value);
+
+            statement2.bindLong(1, 5678);
+            statement1.simpleQueryForString();
+            fail("shouldn't get here");
+        } catch (SQLiteDoneException e) {
+            // expected
+        }
+
+        statement1.close();
+        statement2.close();
+    }
+
+    @MediumTest
+    public void testStatementLongBinding() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
+        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+        for (int i = 0; i < 10; i++) {
+            statement.bindLong(1, i);
+            statement.execute();
+        }
+        statement.close();
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+        int numCol = c.getColumnIndexOrThrow("num");
+        c.moveToFirst();
+        for (long i = 0; i < 10; i++) {
+            long num = c.getLong(numCol);
+            assertEquals(i, num);
+            c.moveToNext();
+        }
+        c.close();
+    }
+
+    @MediumTest
+    public void testStatementStringBinding() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num TEXT);");
+        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+        for (long i = 0; i < 10; i++) {
+            statement.bindString(1, Long.toHexString(i));
+            statement.execute();
+        }
+        statement.close();
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+        int numCol = c.getColumnIndexOrThrow("num");
+        c.moveToFirst();
+        for (long i = 0; i < 10; i++) {
+            String num = c.getString(numCol);
+            assertEquals(Long.toHexString(i), num);
+            c.moveToNext();
+        }
+        c.close();
+    }
+
+    @MediumTest
+    public void testStatementClearBindings() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
+        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+        for (long i = 0; i < 10; i++) {
+            statement.bindLong(1, i);
+            statement.clearBindings();
+            statement.execute();
+        }
+        statement.close();
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
+        int numCol = c.getColumnIndexOrThrow("num");
+        assertTrue(c.moveToFirst());
+        for (long i = 0; i < 10; i++) {
+            assertTrue(c.isNull(numCol));
+            c.moveToNext();
+        }
+        c.close();
+    }
+
+    @MediumTest
+    public void testSimpleStringBinding() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num TEXT, value TEXT);");
+        String statement = "INSERT INTO test (num, value) VALUES (?,?)";
+
+        String[] args = new String[2];
+        for (int i = 0; i < 2; i++) {
+            args[i] = Integer.toHexString(i);
+        }
+
+        mDatabase.execSQL(statement, args);
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+        int numCol = c.getColumnIndexOrThrow("num");
+        int valCol = c.getColumnIndexOrThrow("value");
+        c.moveToFirst();
+        String num = c.getString(numCol);
+        assertEquals(Integer.toHexString(0), num);
+
+        String val = c.getString(valCol);
+        assertEquals(Integer.toHexString(1), val);
+        c.close();
+    }
+
+    @MediumTest
+    public void testStatementMultipleBindings() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num INTEGER, str TEXT);");
+        SQLiteStatement statement =
+                mDatabase.compileStatement("INSERT INTO test (num, str) VALUES (?, ?)");
+
+        for (long i = 0; i < 10; i++) {
+            statement.bindLong(1, i);
+            statement.bindString(2, Long.toHexString(i));
+            statement.execute();
+        }
+        statement.close();
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
+        int numCol = c.getColumnIndexOrThrow("num");
+        int strCol = c.getColumnIndexOrThrow("str");
+        assertTrue(c.moveToFirst());
+        for (long i = 0; i < 10; i++) {
+            long num = c.getLong(numCol);
+            String str = c.getString(strCol);
+            assertEquals(i, num);
+            assertEquals(Long.toHexString(i), str);
+            c.moveToNext();
+        }
+        c.close();
+    }
+
+    private static class StatementTestThread extends Thread {
+        private SQLiteDatabase mDatabase;
+        private SQLiteStatement mStatement;
+
+        public StatementTestThread(SQLiteDatabase db, SQLiteStatement statement) {
+            super();
+            mDatabase = db;
+            mStatement = statement;
+        }
+
+        @Override
+        public void run() {
+            mDatabase.beginTransaction();
+            for (long i = 0; i < 10; i++) {
+                mStatement.bindLong(1, i);
+                mStatement.bindString(2, Long.toHexString(i));
+                mStatement.execute();
+            }
+            mDatabase.setTransactionSuccessful();
+            mDatabase.endTransaction();
+
+            Cursor c = mDatabase.query("test", null, null, null, null, null, "ROWID");
+            int numCol = c.getColumnIndexOrThrow("num");
+            int strCol = c.getColumnIndexOrThrow("str");
+            assertTrue(c.moveToFirst());
+            for (long i = 0; i < 10; i++) {
+                long num = c.getLong(numCol);
+                String str = c.getString(strCol);
+                assertEquals(i, num);
+                assertEquals(Long.toHexString(i), str);
+                c.moveToNext();
+            }
+            c.close();
+        }
+    }
+
+    @MediumTest
+    public void testStatementMultiThreaded() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num INTEGER, str TEXT);");
+        SQLiteStatement statement =
+                mDatabase.compileStatement("INSERT INTO test (num, str) VALUES (?, ?)");
+
+        StatementTestThread thread = new StatementTestThread(mDatabase, statement);
+        thread.start();
+        try {
+            thread.join();
+        } finally {
+            statement.close();
+        }
+    }
+
+    @MediumTest
+    public void testStatementConstraint() throws Exception {
+        mDatabase.execSQL("CREATE TABLE test (num INTEGER NOT NULL);");
+        SQLiteStatement statement = mDatabase.compileStatement("INSERT INTO test (num) VALUES (?)");
+
+        // Try to insert NULL, which violates the constraint
+        try {
+            statement.clearBindings();
+            statement.execute();
+            fail("expected exception not thrown");
+        } catch (SQLiteConstraintException e) {
+            // expected
+        }
+
+        // Make sure the statement can still be used
+        statement.bindLong(1, 1);
+        statement.execute();
+        statement.close();
+
+        Cursor c = mDatabase.query("test", null, null, null, null, null, null);
+        int numCol = c.getColumnIndexOrThrow("num");
+        c.moveToFirst();
+        long num = c.getLong(numCol);
+        assertEquals(1, num);
+        c.close();
+    }
+}
diff --git a/core/tests/coretests/src/android/database/DatabaseStressTest.java b/core/tests/coretests/src/android/database/DatabaseStressTest.java
new file mode 100644
index 0000000..30e46e7
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseStressTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.database;
+
+import android.content.Context;
+import android.database.sqlite.*;
+import android.util.Log;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.Suppress;
+
+import java.io.File;
+
+// This test suite is too desctructive and takes too long to be included in the
+// automated suite.
+@Suppress
+public class DatabaseStressTest extends AndroidTestCase {
+    private static final String TAG = "DatabaseStressTest";    
+    private static final int CURRENT_DATABASE_VERSION = 1;
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        Context c = getContext();
+        
+        mDatabaseFile = c.getDatabasePath("database_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+                
+        mDatabase = c.openOrCreateDatabase("database_test.db", 0, null);            
+       
+        assertNotNull(mDatabase);
+        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
+        
+        mDatabase.execSQL("CREATE TABLE IF NOT EXISTS test (_id INTEGER PRIMARY KEY, data TEXT);");
+
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    public void testSingleThreadInsertDelete() {        
+        int i = 0;
+        char[] ch = new char[100000];
+        String str = new String(ch);
+        String[] strArr = new String[1];
+        strArr[0] = str;
+        for (; i < 10000; ++i) {
+            try {
+                mDatabase.execSQL("INSERT INTO test (data) VALUES (?)", strArr);
+                mDatabase.execSQL("delete from test;");
+            } catch (Exception e) {
+                Log.e(TAG, "exception " + e.getMessage());                
+            }
+        }        
+    }
+   
+    /**
+     * use fillup -p 90 before run the test
+     * and when disk run out
+     * start delete some fillup files
+     * and see if db recover
+     */
+    public void testOutOfSpace() {
+        int i = 0;
+        char[] ch = new char[100000];
+        String str = new String(ch);
+        String[] strArr = new String[1];
+        strArr[0] = str;
+        for (; i < 10000; ++i) {
+            try {
+                mDatabase.execSQL("INSERT INTO test (data) VALUES (?)", strArr);
+            } catch (Exception e) {
+                Log.e(TAG, "exception " + e.getMessage());                
+            }
+        }        
+    }
+}
diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java
new file mode 100644
index 0000000..1349844
--- /dev/null
+++ b/core/tests/coretests/src/android/net/LocalSocketTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import android.net.Credentials;
+import android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public class LocalSocketTest extends TestCase {
+
+    @SmallTest
+    public void testBasic() throws Exception {
+        LocalServerSocket ss;
+        LocalSocket ls;
+        LocalSocket ls1;
+
+        ss = new LocalServerSocket("android.net.LocalSocketTest");
+
+        ls = new LocalSocket();
+
+        ls.connect(new LocalSocketAddress("android.net.LocalSocketTest"));
+
+        ls1 = ss.accept();
+
+        // Test trivial read and write
+        ls.getOutputStream().write(42);
+
+        assertEquals(42, ls1.getInputStream().read());
+
+        // Test getting credentials
+        Credentials c = ls1.getPeerCredentials();
+
+        MoreAsserts.assertNotEqual(0, c.getPid());
+
+        // Test sending and receiving file descriptors
+        ls.setFileDescriptorsForSend(
+                new FileDescriptor[]{FileDescriptor.in});
+
+        ls.getOutputStream().write(42);
+
+        assertEquals(42, ls1.getInputStream().read());
+
+        FileDescriptor[] out = ls1.getAncillaryFileDescriptors();
+
+        assertEquals(1, out.length);
+
+        // Test multible byte write and available()
+        ls1.getOutputStream().write(new byte[]{0, 1, 2, 3, 4, 5}, 1, 5);
+
+        assertEquals(1, ls.getInputStream().read());
+        assertEquals(4, ls.getInputStream().available());
+
+        byte[] buffer = new byte[16];
+        int countRead;
+
+        countRead = ls.getInputStream().read(buffer, 1, 15);
+
+        assertEquals(4, countRead);
+        assertEquals(2, buffer[1]);
+        assertEquals(3, buffer[2]);
+        assertEquals(4, buffer[3]);
+        assertEquals(5, buffer[4]);
+
+        // Try various array-out-of-bound cases
+        try {
+            ls.getInputStream().read(buffer, 1, 16);
+            fail("expected exception");
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // excpected
+        }
+
+        try {
+            ls.getOutputStream().write(buffer, 1, 16);
+            fail("expected exception");
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // excpected
+        }
+
+        try {
+            ls.getOutputStream().write(buffer, -1, 15);
+            fail("expected exception");
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // excpected
+        }
+
+        try {
+            ls.getOutputStream().write(buffer, 0, -1);
+            fail("expected exception");
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // excpected
+        }
+
+        try {
+            ls.getInputStream().read(buffer, -1, 15);
+            fail("expected exception");
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // excpected
+        }
+
+        try {
+            ls.getInputStream().read(buffer, 0, -1);
+            fail("expected exception");
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            // excpected
+        }
+
+        // Try read of length 0
+        ls.getOutputStream().write(42);
+        countRead = ls1.getInputStream().read(buffer, 0, 0);
+        assertEquals(0, countRead);
+        assertEquals(42, ls1.getInputStream().read());
+
+        ss.close();
+
+        ls.close();
+
+        // Try write on closed socket
+
+        try {
+            ls.getOutputStream().write(42);
+            fail("expected exception");
+        } catch (IOException ex) {
+            // Expected
+        }
+
+        // Try read on closed socket
+
+        try {
+            ls.getInputStream().read();
+            fail("expected exception");
+        } catch (IOException ex) {
+            // Expected
+        }
+
+        // Try write on socket whose peer has closed
+
+        try {
+            ls1.getOutputStream().write(42);
+            fail("expected exception");
+        } catch (IOException ex) {
+            // Expected
+        }
+
+        // Try read on socket whose peer has closed
+
+        assertEquals(-1, ls1.getInputStream().read());
+
+        ls1.close();
+    }
+}
diff --git a/core/tests/coretests/src/android/net/SSLTest.java b/core/tests/coretests/src/android/net/SSLTest.java
new file mode 100644
index 0000000..810ed0d
--- /dev/null
+++ b/core/tests/coretests/src/android/net/SSLTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.net.SSLCertificateSocketFactory;
+import android.test.suitebuilder.annotation.Suppress;
+import junit.framework.TestCase;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+//This test relies on network resources.
+@Suppress
+public class SSLTest extends TestCase {
+    public void testCertificate() throws Exception {
+        // test www.fortify.net/sslcheck.html
+        Socket ssl = SSLCertificateSocketFactory.getDefault().createSocket("www.fortify.net",443);
+        assertNotNull(ssl);
+
+        OutputStream out = ssl.getOutputStream();
+        assertNotNull(out);
+
+        InputStream in = ssl.getInputStream();
+        assertNotNull(in);
+
+        String get = "GET /sslcheck.html HTTP/1.1\r\nHost: 68.178.217.222\r\n\r\n";
+
+        // System.out.println("going for write...");
+        out.write(get.getBytes());
+
+        byte[] b = new byte[1024];
+        // System.out.println("going for read...");
+        int ret = in.read(b);
+
+        // System.out.println(new String(b));
+    }
+}
diff --git a/core/tests/coretests/src/android/net/UriMatcherTest.java b/core/tests/coretests/src/android/net/UriMatcherTest.java
new file mode 100644
index 0000000..2872144
--- /dev/null
+++ b/core/tests/coretests/src/android/net/UriMatcherTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+public class UriMatcherTest extends TestCase
+{
+    static final int ROOT = 0;
+    static final int PEOPLE = 1;
+    static final int PEOPLE_ID = 2;
+    static final int PEOPLE_PHONES = 3;
+    static final int PEOPLE_PHONES_ID = 4;
+    static final int PEOPLE_ADDRESSES = 5;
+    static final int PEOPLE_ADDRESSES_ID = 6;
+    static final int PEOPLE_CONTACTMETH = 7;
+    static final int PEOPLE_CONTACTMETH_ID = 8;
+    static final int CALLS = 9;
+    static final int CALLS_ID = 10;
+    static final int CALLERID = 11;
+    static final int CALLERID_TEXT = 12;
+    static final int FILTERRECENT = 13;
+    
+    @SmallTest
+    public void testContentUris() {
+        check("content://asdf", UriMatcher.NO_MATCH);
+        check("content://people", PEOPLE);
+        check("content://people/1", PEOPLE_ID);
+        check("content://people/asdf", UriMatcher.NO_MATCH);
+        check("content://people/2/phones", PEOPLE_PHONES); 
+        check("content://people/2/phones/3", PEOPLE_PHONES_ID); 
+        check("content://people/2/phones/asdf", UriMatcher.NO_MATCH);
+        check("content://people/2/addresses", PEOPLE_ADDRESSES); 
+        check("content://people/2/addresses/3", PEOPLE_ADDRESSES_ID); 
+        check("content://people/2/addresses/asdf", UriMatcher.NO_MATCH);
+        check("content://people/2/contact-methods", PEOPLE_CONTACTMETH); 
+        check("content://people/2/contact-methods/3", PEOPLE_CONTACTMETH_ID); 
+        check("content://people/2/contact-methods/asdf", UriMatcher.NO_MATCH);
+        check("content://calls", CALLS);
+        check("content://calls/1", CALLS_ID);
+        check("content://calls/asdf", UriMatcher.NO_MATCH);
+        check("content://caller-id", CALLERID);
+        check("content://caller-id/asdf", CALLERID_TEXT);
+        check("content://caller-id/1", CALLERID_TEXT);
+        check("content://filter-recent", FILTERRECENT);
+    }
+
+    private static final UriMatcher mURLMatcher = new UriMatcher(ROOT);
+
+    static
+    {
+        mURLMatcher.addURI("people", null, PEOPLE);
+        mURLMatcher.addURI("people", "#", PEOPLE_ID);
+        mURLMatcher.addURI("people", "#/phones", PEOPLE_PHONES);
+        mURLMatcher.addURI("people", "#/phones/blah", PEOPLE_PHONES_ID);
+        mURLMatcher.addURI("people", "#/phones/#", PEOPLE_PHONES_ID);
+        mURLMatcher.addURI("people", "#/addresses", PEOPLE_ADDRESSES);
+        mURLMatcher.addURI("people", "#/addresses/#", PEOPLE_ADDRESSES_ID);
+        mURLMatcher.addURI("people", "#/contact-methods", PEOPLE_CONTACTMETH);
+        mURLMatcher.addURI("people", "#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
+        mURLMatcher.addURI("calls", null, CALLS);
+        mURLMatcher.addURI("calls", "#", CALLS_ID);
+        mURLMatcher.addURI("caller-id", null, CALLERID);
+        mURLMatcher.addURI("caller-id", "*", CALLERID_TEXT);
+        mURLMatcher.addURI("filter-recent", null, FILTERRECENT);
+    }
+
+    void check(String uri, int expected)
+    {
+        int result = mURLMatcher.match(Uri.parse(uri));
+        if (result != expected) {
+            String msg = "failed on " + uri;
+            msg += " expected " + expected + " got " + result;
+            throw new RuntimeException(msg);
+        }
+    }
+}
+
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
new file mode 100644
index 0000000..ad71fcb
--- /dev/null
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -0,0 +1,578 @@
+/*
+ * 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.
+ */
+
+package android.net;
+
+import android.net.Uri;
+import android.content.ContentUris;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.util.Arrays;
+
+public class UriTest extends TestCase {
+
+    @SmallTest
+    public void testToStringWithPathOnly() {
+        Uri.Builder builder = new Uri.Builder();
+
+        // Not a valid path, but this came from a user's test case.
+        builder.path("//foo");
+        Uri uri = builder.build();
+        assertEquals("//foo", uri.toString());
+    }
+
+    @SmallTest
+    public void testParcelling() {
+        parcelAndUnparcel(Uri.parse("foo:bob%20lee"));
+        parcelAndUnparcel(Uri.fromParts("foo", "bob lee", "fragment"));
+        parcelAndUnparcel(new Uri.Builder()
+            .scheme("http")
+            .authority("crazybob.org")
+            .path("/rss/")
+            .encodedQuery("a=b")
+            .fragment("foo")
+            .build());
+    }
+
+    private void parcelAndUnparcel(Uri u) {
+        Parcel p = Parcel.obtain();
+	try {
+		Uri.writeToParcel(p, u);
+		p.setDataPosition(0);
+		assertEquals(u, Uri.CREATOR.createFromParcel(p));
+
+		p.setDataPosition(0);
+		u = u.buildUpon().build();        
+		Uri.writeToParcel(p, u);
+		p.setDataPosition(0);
+		assertEquals(u, Uri.CREATOR.createFromParcel(p));
+	}
+	finally {
+		p.recycle();
+	}
+    }
+
+    @SmallTest
+    public void testBuildUponOpaqueStringUri() {
+        Uri u = Uri.parse("bob:lee").buildUpon().scheme("robert").build();
+        assertEquals("robert", u.getScheme());
+        assertEquals("lee", u.getEncodedSchemeSpecificPart());
+        assertEquals("lee", u.getSchemeSpecificPart());
+        assertNull(u.getQuery());
+        assertNull(u.getPath());
+        assertNull(u.getAuthority());
+        assertNull(u.getHost());
+    }
+
+    @SmallTest
+    public void testStringUri() {
+        assertEquals("bob lee",
+                Uri.parse("foo:bob%20lee").getSchemeSpecificPart());
+        assertEquals("bob%20lee",
+                Uri.parse("foo:bob%20lee").getEncodedSchemeSpecificPart());
+        assertEquals("/bob%20lee",
+                Uri.parse("foo:/bob%20lee").getEncodedPath());
+        assertNull(Uri.parse("foo:bob%20lee").getPath());
+        assertEquals("bob%20lee",
+                Uri.parse("foo:?bob%20lee").getEncodedQuery());
+        assertNull(Uri.parse("foo:bar#?bob%20lee").getQuery());
+        assertEquals("bob%20lee",
+                Uri.parse("foo:#bob%20lee").getEncodedFragment());
+    }
+
+    @SmallTest
+    public void testStringUriIsHierarchical() {
+        assertTrue(Uri.parse("bob").isHierarchical());
+        assertFalse(Uri.parse("bob:").isHierarchical());
+    }
+
+    @SmallTest
+    public void testNullUriString() {
+        try {
+            Uri.parse(null);
+            fail();
+        } catch (NullPointerException e) {}
+    }
+
+    @SmallTest
+    public void testNullFile() {
+        try {
+            Uri.fromFile(null);
+            fail();
+        } catch (NullPointerException e) {}
+    }
+
+    @SmallTest
+    public void testCompareTo() {
+        Uri a = Uri.parse("foo:a");
+        Uri b = Uri.parse("foo:b");
+        Uri b2 = Uri.parse("foo:b");
+
+        assertTrue(a.compareTo(b) < 0);
+        assertTrue(b.compareTo(a) > 0);
+        assertEquals(0, b.compareTo(b2));
+    }
+
+    @SmallTest
+    public void testEqualsAndHashCode() {
+
+        Uri a = Uri.parse("http://crazybob.org/test/?foo=bar#tee");
+
+        Uri b = new Uri.Builder()
+                .scheme("http")
+                .authority("crazybob.org")
+                .path("/test/")
+                .encodedQuery("foo=bar")
+                .fragment("tee")
+                .build();
+
+        // Try alternate builder methods.
+        Uri c = new Uri.Builder()
+                .scheme("http")
+                .encodedAuthority("crazybob.org")
+                .encodedPath("/test/")
+                .encodedQuery("foo=bar")
+                .encodedFragment("tee")
+                .build();
+
+        assertFalse(Uri.EMPTY.equals(null));
+
+        assertEquals(a, b);
+        assertEquals(b, c);
+        assertEquals(c, a);
+
+        assertEquals(a.hashCode(), b.hashCode());
+        assertEquals(b.hashCode(), c.hashCode());
+    }
+
+    @SmallTest
+    public void testAuthorityParsing() {
+        Uri uri = Uri.parse("http://localhost:42");
+        assertEquals("localhost", uri.getHost());
+        assertEquals(42, uri.getPort());
+
+        uri = Uri.parse("http://bob@localhost:42");
+        assertEquals("bob", uri.getUserInfo());
+        assertEquals("localhost", uri.getHost());
+        assertEquals(42, uri.getPort());
+
+        uri = Uri.parse("http://bob%20lee@localhost:42");
+        assertEquals("bob lee", uri.getUserInfo());
+        assertEquals("bob%20lee", uri.getEncodedUserInfo());
+
+        uri = Uri.parse("http://bob%40lee%3ajr@local%68ost:4%32");
+        assertEquals("bob@lee:jr", uri.getUserInfo());
+        assertEquals("localhost", uri.getHost());
+        assertEquals(42, uri.getPort());
+
+        uri = Uri.parse("http://localhost");
+        assertEquals("localhost", uri.getHost());
+        assertEquals(-1, uri.getPort());
+    }
+
+    @SmallTest
+    public void testBuildUponOpaqueUri() {
+        Uri a = Uri.fromParts("foo", "bar", "tee");
+        Uri b = a.buildUpon().fragment("new").build();
+        assertEquals("new", b.getFragment());
+        assertEquals("bar", b.getSchemeSpecificPart());
+        assertEquals("foo", b.getScheme());        
+    }
+
+    @SmallTest
+    public void testBuildUponEncodedOpaqueUri() {
+        Uri a = new Uri.Builder()
+                .scheme("foo")
+                .encodedOpaquePart("bar")
+                .fragment("tee")
+                .build();
+        Uri b = a.buildUpon().fragment("new").build();
+        assertEquals("new", b.getFragment());
+        assertEquals("bar", b.getSchemeSpecificPart());
+        assertEquals("foo", b.getScheme());
+    }
+
+    @SmallTest
+    public void testPathSegmentDecoding() {
+        Uri uri = Uri.parse("foo://bar/a%20a/b%20b");
+        assertEquals("a a", uri.getPathSegments().get(0));
+        assertEquals("b b", uri.getPathSegments().get(1));
+    }
+
+    @SmallTest
+    public void testSms() {
+        Uri base = Uri.parse("content://sms");
+        Uri appended = base.buildUpon()
+                .appendEncodedPath("conversations/addr=555-1212")
+                .build();
+        assertEquals("content://sms/conversations/addr=555-1212",
+                appended.toString());
+        assertEquals(2, appended.getPathSegments().size());
+        assertEquals("conversations", appended.getPathSegments().get(0));
+        assertEquals("addr=555-1212", appended.getPathSegments().get(1));
+    }
+
+    @SmallTest
+    public void testEncodeWithAllowedChars() {
+        String encoded = Uri.encode("Bob:/", "/");
+        assertEquals(-1, encoded.indexOf(':'));
+        assertTrue(encoded.indexOf('/') > -1);
+    }
+
+    @SmallTest
+    public void testEncodeDecode() {
+        code(null);
+        code("");
+        code("Bob");
+        code(":Bob");
+        code("::Bob");
+        code("Bob::Lee");
+        code("Bob:Lee");
+        code("Bob::");
+        code("Bob:");
+        code("::Bob::");
+    }
+
+    private void code(String s) {
+        assertEquals(s, Uri.decode(Uri.encode(s, null)));
+    }
+
+    @SmallTest
+    public void testFile() {
+        File f = new File("/tmp/bob");
+
+        Uri uri = Uri.fromFile(f);
+
+        assertEquals("file:///tmp/bob", uri.toString());
+    }
+
+    @SmallTest
+    public void testQueryParameters() {
+        Uri uri = Uri.parse("content://user");
+
+        assertEquals(null, uri.getQueryParameter("a"));
+
+        uri = uri.buildUpon().appendQueryParameter("a", "b").build();
+
+        assertEquals("b", uri.getQueryParameter("a"));
+
+        uri = uri.buildUpon().appendQueryParameter("a", "b2").build();
+
+        assertEquals(Arrays.asList("b", "b2"), uri.getQueryParameters("a"));
+
+        uri = uri.buildUpon().appendQueryParameter("c", "d").build();
+
+        assertEquals(Arrays.asList("b", "b2"), uri.getQueryParameters("a"));
+        assertEquals("d", uri.getQueryParameter("c"));
+    }
+
+    @SmallTest
+    public void testSchemeOnly() {
+        Uri uri = Uri.parse("empty:");
+        assertEquals("empty", uri.getScheme());
+        assertTrue(uri.isAbsolute());
+        assertNull(uri.getPath());
+    }
+
+    @SmallTest
+    public void testEmptyPath() {
+        Uri uri = Uri.parse("content://user");
+        assertEquals(0, uri.getPathSegments().size());
+    }
+
+    @SmallTest
+    public void testPathOperations() {
+        Uri uri = Uri.parse("content://user/a/b");
+
+        assertEquals(2, uri.getPathSegments().size());
+        assertEquals("b", uri.getLastPathSegment());
+
+        Uri first = uri;
+        uri = uri.buildUpon().appendPath("c").build();
+
+        assertEquals(3, uri.getPathSegments().size());
+        assertEquals("c", uri.getLastPathSegment());
+        assertEquals("content://user/a/b/c", uri.toString());
+
+        uri = ContentUris.withAppendedId(uri, 100);
+
+        assertEquals(4, uri.getPathSegments().size());
+        assertEquals("100", uri.getLastPathSegment());
+        assertEquals(100, ContentUris.parseId(uri));
+        assertEquals("content://user/a/b/c/100", uri.toString());
+
+        // Make sure the original URI is still intact.
+        assertEquals(2, first.getPathSegments().size());
+        assertEquals("b", first.getLastPathSegment());
+
+        try {
+            first.getPathSegments().get(2);
+            fail();
+        } catch (IndexOutOfBoundsException e) {}
+
+        assertEquals(null, Uri.EMPTY.getLastPathSegment());
+
+        Uri withC = Uri.parse("foo:/a/b/").buildUpon().appendPath("c").build();
+        assertEquals("/a/b/c", withC.getPath());
+    }
+
+    @SmallTest
+    public void testOpaqueUri() {
+        Uri uri = Uri.parse("mailto:nobody");
+        testOpaqueUri(uri);
+
+        uri = uri.buildUpon().build();
+        testOpaqueUri(uri);
+
+        uri = Uri.fromParts("mailto", "nobody", null);
+        testOpaqueUri(uri);
+
+        uri = uri.buildUpon().build();
+        testOpaqueUri(uri);
+
+        uri = new Uri.Builder()
+                .scheme("mailto")
+                .opaquePart("nobody")
+                .build();
+        testOpaqueUri(uri);
+
+        uri = uri.buildUpon().build();
+        testOpaqueUri(uri);
+    }
+
+    private void testOpaqueUri(Uri uri) {
+        assertEquals("mailto", uri.getScheme());
+        assertEquals("nobody", uri.getSchemeSpecificPart());
+        assertEquals("nobody", uri.getEncodedSchemeSpecificPart());
+
+        assertNull(uri.getFragment());
+        assertTrue(uri.isAbsolute());
+        assertTrue(uri.isOpaque());
+        assertFalse(uri.isRelative());
+        assertFalse(uri.isHierarchical());
+
+        assertNull(uri.getAuthority());
+        assertNull(uri.getEncodedAuthority());
+        assertNull(uri.getPath());
+        assertNull(uri.getEncodedPath());
+        assertNull(uri.getUserInfo());
+        assertNull(uri.getEncodedUserInfo());
+        assertNull(uri.getQuery());
+        assertNull(uri.getEncodedQuery());
+        assertNull(uri.getHost());
+        assertEquals(-1, uri.getPort());
+
+        assertTrue(uri.getPathSegments().isEmpty());
+        assertNull(uri.getLastPathSegment());
+
+        assertEquals("mailto:nobody", uri.toString());
+
+        Uri withFragment = uri.buildUpon().fragment("top").build();
+        assertEquals("mailto:nobody#top", withFragment.toString());
+    }
+
+    @SmallTest
+    public void testHierarchicalUris() {
+        testHierarchical("http", "google.com", "/p1/p2", "query", "fragment");
+        testHierarchical("file", null, "/p1/p2", null, null);
+        testHierarchical("content", "contact", "/p1/p2", null, null);
+        testHierarchical("http", "google.com", "/p1/p2", null, "fragment");
+        testHierarchical("http", "google.com", "", null, "fragment");
+        testHierarchical("http", "google.com", "", "query", "fragment");
+        testHierarchical("http", "google.com", "", "query", null);
+        testHierarchical("http", null, "/", "query", null);
+    }
+
+    private static void testHierarchical(String scheme, String authority,
+            String path, String query, String fragment) {
+        StringBuilder sb = new StringBuilder();
+
+        if (authority != null) {
+            sb.append("//").append(authority);
+        }
+        if (path != null) {
+            sb.append(path);
+        }
+        if (query != null) {
+            sb.append('?').append(query);
+        }
+
+        String ssp = sb.toString();
+
+        if (scheme != null) {
+            sb.insert(0, scheme + ":");
+        }
+        if (fragment != null) {
+            sb.append('#').append(fragment);
+        }
+
+        String uriString = sb.toString();
+
+        Uri uri = Uri.parse(uriString);
+
+        // Run these twice to test caching.
+        compareHierarchical(
+                uriString, ssp, uri, scheme, authority, path, query, fragment);
+        compareHierarchical(
+                uriString, ssp, uri, scheme, authority, path, query, fragment);
+
+        // Test rebuilt version.
+        uri = uri.buildUpon().build();
+
+        // Run these twice to test caching.
+        compareHierarchical(
+                uriString, ssp, uri, scheme, authority, path, query, fragment);
+        compareHierarchical(
+                uriString, ssp, uri, scheme, authority, path, query, fragment);
+
+        // The decoded and encoded versions of the inputs are all the same.
+        // We'll test the actual encoding decoding separately.
+
+        // Test building with encoded versions.
+        Uri built = new Uri.Builder()
+                .scheme(scheme)
+                .encodedAuthority(authority)
+                .encodedPath(path)
+                .encodedQuery(query)
+                .encodedFragment(fragment)
+                .build();
+
+        compareHierarchical(
+                uriString, ssp, built, scheme, authority, path, query, fragment);
+        compareHierarchical(
+                uriString, ssp, built, scheme, authority, path, query, fragment);
+
+        // Test building with decoded versions.
+        built = new Uri.Builder()
+                .scheme(scheme)
+                .authority(authority)
+                .path(path)
+                .query(query)
+                .fragment(fragment)
+                .build();
+
+        compareHierarchical(
+                uriString, ssp, built, scheme, authority, path, query, fragment);
+        compareHierarchical(
+                uriString, ssp, built, scheme, authority, path, query, fragment);
+
+        // Rebuild.
+        built = built.buildUpon().build();
+
+        compareHierarchical(
+                uriString, ssp, built, scheme, authority, path, query, fragment);
+        compareHierarchical(
+                uriString, ssp, built, scheme, authority, path, query, fragment);
+    }
+
+    private static void compareHierarchical(String uriString, String ssp,
+            Uri uri,
+            String scheme, String authority, String path, String query,
+            String fragment) {
+        assertEquals(scheme, uri.getScheme());
+        assertEquals(authority, uri.getAuthority());
+        assertEquals(authority, uri.getEncodedAuthority());
+        assertEquals(path, uri.getPath());
+        assertEquals(path, uri.getEncodedPath());
+        assertEquals(query, uri.getQuery());
+        assertEquals(query, uri.getEncodedQuery());
+        assertEquals(fragment, uri.getFragment());
+        assertEquals(fragment, uri.getEncodedFragment());
+        assertEquals(ssp, uri.getSchemeSpecificPart());
+
+        if (scheme != null) {
+            assertTrue(uri.isAbsolute());
+            assertFalse(uri.isRelative());
+        } else {
+            assertFalse(uri.isAbsolute());
+            assertTrue(uri.isRelative());
+        }
+
+        assertFalse(uri.isOpaque());
+        assertTrue(uri.isHierarchical());
+
+        assertEquals(uriString, uri.toString());
+    }
+
+    public void testEmptyToStringNotNull() {
+        assertNotNull(Uri.EMPTY.toString());
+    }
+
+    @SmallTest
+    public void testParcellingWithoutFragment() {
+        parcelAndUnparcel(Uri.parse("foo:bob%20lee"));
+        parcelAndUnparcel(Uri.fromParts("foo", "bob lee", "fragment"));
+        parcelAndUnparcel(new Uri.Builder()
+            .scheme("http")
+            .authority("crazybob.org")
+            .path("/rss/")
+            .encodedQuery("a=b")
+            .build());
+    }
+
+    public void testGetQueryParameter() {
+        String nestedUrl = "http://crazybob.org/?a=1&b=2";
+        Uri uri = Uri.parse("http://test/").buildUpon()
+                .appendQueryParameter("foo", "bar")
+                .appendQueryParameter("nested", nestedUrl).build();
+        assertEquals(nestedUrl, uri.getQueryParameter("nested"));
+        assertEquals(nestedUrl, uri.getQueryParameters("nested").get(0));
+    }
+
+    public void testGetQueryParameterWorkaround() {
+        // This was a workaround for a bug where getQueryParameter called
+        // getQuery() instead of getEncodedQuery().
+        String nestedUrl = "http://crazybob.org/?a=1&b=2";
+        Uri uri = Uri.parse("http://test/").buildUpon()
+                .appendQueryParameter("foo", "bar")
+                .appendQueryParameter("nested", Uri.encode(nestedUrl)).build();
+        assertEquals(nestedUrl, Uri.decode(uri.getQueryParameter("nested")));
+        assertEquals(nestedUrl,
+                Uri.decode(uri.getQueryParameters("nested").get(0)));
+    }
+
+    public void testGetQueryParameterEdgeCases() {
+        Uri uri;
+
+        // key at beginning of URL
+        uri = Uri.parse("http://test/").buildUpon()
+            .appendQueryParameter("key", "a b")
+            .appendQueryParameter("keya", "c d")
+            .appendQueryParameter("bkey", "e f")
+            .build();
+        assertEquals("a b", uri.getQueryParameter("key"));
+
+        // key in middle of URL
+        uri = Uri.parse("http://test/").buildUpon()
+            .appendQueryParameter("akeyb", "a b")
+            .appendQueryParameter("keya", "c d")
+            .appendQueryParameter("key", "e f")
+            .appendQueryParameter("bkey", "g h")
+            .build();
+        assertEquals("e f", uri.getQueryParameter("key"));
+
+        // key at end of URL
+        uri = Uri.parse("http://test/").buildUpon()
+            .appendQueryParameter("akeyb", "a b")
+            .appendQueryParameter("keya", "c d")
+            .appendQueryParameter("key", "y z")
+            .build();
+        assertEquals("y z", uri.getQueryParameter("key"));
+    }
+}
diff --git a/core/tests/coretests/src/android/os/AidlTest.aidl b/core/tests/coretests/src/android/os/AidlTest.aidl
new file mode 100644
index 0000000..6004f4b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/AidlTest.aidl
@@ -0,0 +1,20 @@
+/* //device/apps/AndroidTests/src/com.android.unit_tests/AidlTest.aidl
+**
+** Copyright 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.
+*/
+
+package android.os;
+
+parcelable AidlTest.TestParcelable;
diff --git a/core/tests/coretests/src/android/os/AidlTest.java b/core/tests/coretests/src/android/os/AidlTest.java
new file mode 100644
index 0000000..bf11d56
--- /dev/null
+++ b/core/tests/coretests/src/android/os/AidlTest.java
@@ -0,0 +1,422 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.os.IInterface;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.google.android.collect.Lists;
+import junit.framework.TestCase;
+
+import java.util.List;
+
+public class AidlTest extends TestCase {
+
+    private IAidlTest mRemote;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        AidlObject mLocal = new AidlObject();
+        mRemote = IAidlTest.Stub.asInterface(mLocal);
+    }
+
+    private static boolean check(TestParcelable p, int n, String s) {
+        return p.mAnInt == n &&
+                ((s == null && p.mAString == null) || s.equals(p.mAString));
+    }
+
+    public static class TestParcelable implements Parcelable {
+        public int mAnInt;
+        public String mAString;
+
+        public TestParcelable() {
+        }
+
+        public TestParcelable(int i, String s) {
+            mAnInt = i;
+            mAString = s;
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel parcel, int flags) {
+            parcel.writeInt(mAnInt);
+            parcel.writeString(mAString);
+        }
+
+        public void readFromParcel(Parcel parcel) {
+            mAnInt = parcel.readInt();
+            mAString = parcel.readString();
+        }
+
+        public static final Parcelable.Creator<TestParcelable> CREATOR
+                = new Parcelable.Creator<TestParcelable>() {
+            public TestParcelable createFromParcel(Parcel parcel) {
+                return new TestParcelable(parcel.readInt(),
+                        parcel.readString());
+            }
+
+            public TestParcelable[] newArray(int size) {
+                return new TestParcelable[size];
+            }
+        };
+
+        public String toString() {
+            return super.toString() + " {" + mAnInt + "/" + mAString + "}";
+        }
+    }
+
+    private static class AidlObject extends IAidlTest.Stub {
+        public IInterface queryLocalInterface(String descriptor) {
+            // overriding this to return null makes asInterface always
+            // generate a proxy
+            return null;
+        }
+
+        public int intMethod(int a) {
+            return a;
+        }
+
+        public TestParcelable parcelableIn(TestParcelable p) {
+            p.mAnInt++;
+            return p;
+        }
+
+        public TestParcelable parcelableOut(TestParcelable p) {
+            p.mAnInt = 44;
+            return p;
+        }
+
+        public TestParcelable parcelableInOut(TestParcelable p) {
+            p.mAnInt++;
+            return p;
+        }
+
+        public TestParcelable listParcelableLonger(List<TestParcelable> list, int index) {
+            list.add(list.get(index));
+            return list.get(index);
+        }
+
+        public int listParcelableShorter(List<TestParcelable> list, int index) {
+            list.remove(index);
+            return list.size();
+        }
+
+        public boolean[] booleanArray(boolean[] a0, boolean[] a1, boolean[] a2) {
+            for (int i = 0; i < a0.length && i < a2.length; i++) {
+                a2[i] = a0[i];
+            }
+            for (int i = 0; i < a0.length && i < a1.length; i++) {
+                a1[i] = a0[i];
+            }
+            return a0;
+        }
+
+        public char[] charArray(char[] a0, char[] a1, char[] a2) {
+            for (int i = 0; i < a0.length && i < a2.length; i++) {
+                a2[i] = a0[i];
+            }
+            for (int i = 0; i < a0.length && i < a1.length; i++) {
+                a1[i] = a0[i];
+            }
+            return a0;
+        }
+
+        public int[] intArray(int[] a0, int[] a1, int[] a2) {
+            for (int i = 0; i < a0.length && i < a2.length; i++) {
+                a2[i] = a0[i];
+            }
+            for (int i = 0; i < a0.length && i < a1.length; i++) {
+                a1[i] = a0[i];
+            }
+            return a0;
+        }
+
+        public long[] longArray(long[] a0, long[] a1, long[] a2) {
+            for (int i = 0; i < a0.length && i < a2.length; i++) {
+                a2[i] = a0[i];
+            }
+            for (int i = 0; i < a0.length && i < a1.length; i++) {
+                a1[i] = a0[i];
+            }
+            return a0;
+        }
+
+        public float[] floatArray(float[] a0, float[] a1, float[] a2) {
+            for (int i = 0; i < a0.length && i < a2.length; i++) {
+                a2[i] = a0[i];
+            }
+            for (int i = 0; i < a0.length && i < a1.length; i++) {
+                a1[i] = a0[i];
+            }
+            return a0;
+        }
+
+        public double[] doubleArray(double[] a0, double[] a1, double[] a2) {
+            for (int i = 0; i < a0.length && i < a2.length; i++) {
+                a2[i] = a0[i];
+            }
+            for (int i = 0; i < a0.length && i < a1.length; i++) {
+                a1[i] = a0[i];
+            }
+            return a0;
+        }
+
+        public String[] stringArray(String[] a0, String[] a1, String[] a2) {
+            for (int i = 0; i < a0.length && i < a2.length; i++) {
+                a2[i] = a0[i];
+            }
+            for (int i = 0; i < a0.length && i < a1.length; i++) {
+                a1[i] = a0[i];
+            }
+            return a0;
+        }
+
+        public TestParcelable[] parcelableArray(TestParcelable[] a0,
+                TestParcelable[] a1, TestParcelable[] a2) {
+            return null;
+        }
+        
+        public void voidSecurityException() {
+            throw new SecurityException("gotcha!");
+        }
+
+        public int intSecurityException() {
+            throw new SecurityException("gotcha!");
+        }
+    }
+
+    @SmallTest
+    public void testInt() throws Exception {
+        int result = mRemote.intMethod(42);
+        assertEquals(42, result);
+    }
+
+    @SmallTest
+    public void testParcelableIn() throws Exception {
+        TestParcelable arg = new TestParcelable(43, "hi");
+        TestParcelable result = mRemote.parcelableIn(arg);
+        assertNotSame(arg, result);
+
+        assertEquals(43, arg.mAnInt);
+        assertEquals(44, result.mAnInt);
+    }
+
+    @SmallTest
+    public void testParcelableOut() throws Exception {
+        TestParcelable arg = new TestParcelable(43, "hi");
+        TestParcelable result = mRemote.parcelableOut(arg);
+        assertNotSame(arg, result);
+        assertEquals(44, arg.mAnInt);
+    }
+
+    @SmallTest
+    public void testParcelableInOut() throws Exception {
+        TestParcelable arg = new TestParcelable(43, "hi");
+        TestParcelable result = mRemote.parcelableInOut(arg);
+        assertNotSame(arg, result);
+        assertEquals(44, arg.mAnInt);
+    }
+
+    @SmallTest
+    public void testListParcelableLonger() throws Exception {
+        List<TestParcelable> list = Lists.newArrayList();
+        list.add(new TestParcelable(33, "asdf"));
+        list.add(new TestParcelable(34, "jkl;"));
+
+        TestParcelable result = mRemote.listParcelableLonger(list, 1);
+
+//        System.out.println("result=" + result);
+//        for (TestParcelable p : list) {
+//            System.out.println("longer: " + p);
+//        }
+
+        assertEquals("jkl;", result.mAString);
+        assertEquals(34, result.mAnInt);
+
+        assertEquals(3, list.size());
+        assertTrue("out parameter 0: " + list.get(0), check(list.get(0), 33, "asdf"));
+        assertTrue("out parameter 1: " + list.get(1), check(list.get(1), 34, "jkl;"));
+        assertTrue("out parameter 2: " + list.get(2), check(list.get(2), 34, "jkl;"));
+
+        assertNotSame(list.get(1), list.get(2));
+    }
+
+    @SmallTest
+    public void testListParcelableShorter() throws Exception {
+        List<TestParcelable> list = Lists.newArrayList();
+        list.add(new TestParcelable(33, "asdf"));
+        list.add(new TestParcelable(34, "jkl;"));
+        list.add(new TestParcelable(35, "qwerty"));
+
+        int result = mRemote.listParcelableShorter(list, 2);
+
+//        System.out.println("result=" + result);
+//        for (TestParcelable p : list) {
+//            System.out.println("shorter: " + p);
+//        }
+
+        assertEquals(2, result);
+        assertEquals(2, list.size());
+        assertTrue("out parameter 0: " + list.get(0), check(list.get(0), 33, "asdf"));
+        assertTrue("out parameter 1: " + list.get(1), check(list.get(1), 34, "jkl;"));
+
+        assertNotSame(list.get(0), list.get(1));
+    }
+
+    @SmallTest
+    public void testArrays() throws Exception {
+        // boolean
+        boolean[] b0 = new boolean[]{true};
+        boolean[] b1 = new boolean[]{false, true};
+        boolean[] b2 = new boolean[]{true, false, true};
+        boolean[] br = mRemote.booleanArray(b0, b1, b2);
+
+        assertEquals(1, br.length);
+        assertTrue(br[0]);
+
+        assertTrue(b1[0]);
+        assertFalse(b1[1]);
+
+        assertTrue(b2[0]);
+        assertFalse(b2[1]);
+        assertTrue(b2[2]);
+
+        // char
+        char[] c0 = new char[]{'a'};
+        char[] c1 = new char[]{'b', 'c'};
+        char[] c2 = new char[]{'d', 'e', 'f'};
+        char[] cr = mRemote.charArray(c0, c1, c2);
+
+        assertEquals(1, cr.length);
+        assertEquals('a', cr[0]);
+
+        assertEquals('a', c1[0]);
+        assertEquals('\0', c1[1]);
+
+        assertEquals('a', c2[0]);
+        assertEquals('e', c2[1]);
+        assertEquals('f', c2[2]);
+
+        // int
+        int[] i0 = new int[]{34};
+        int[] i1 = new int[]{38, 39};
+        int[] i2 = new int[]{42, 43, 44};
+        int[] ir = mRemote.intArray(i0, i1, i2);
+
+        assertEquals(1, ir.length);
+        assertEquals(34, ir[0]);
+
+        assertEquals(34, i1[0]);
+        assertEquals(0, i1[1]);
+
+        assertEquals(34, i2[0]);
+        assertEquals(43, i2[1]);
+        assertEquals(44, i2[2]);
+
+        // long
+        long[] l0 = new long[]{50};
+        long[] l1 = new long[]{51, 52};
+        long[] l2 = new long[]{53, 54, 55};
+        long[] lr = mRemote.longArray(l0, l1, l2);
+
+        assertEquals(1, lr.length);
+        assertEquals(50, lr[0]);
+
+        assertEquals(50, l1[0]);
+        assertEquals(0, l1[1]);
+
+        assertEquals(50, l2[0]);
+        assertEquals(54, l2[1]);
+        assertEquals(55, l2[2]);
+
+        // float
+        float[] f0 = new float[]{90.1f};
+        float[] f1 = new float[]{90.2f, 90.3f};
+        float[] f2 = new float[]{90.4f, 90.5f, 90.6f};
+        float[] fr = mRemote.floatArray(f0, f1, f2);
+
+        assertEquals(1, fr.length);
+        assertEquals(90.1f, fr[0]);
+
+        assertEquals(90.1f, f1[0]);
+        assertEquals(0f, f1[1], 0.0f);
+
+        assertEquals(90.1f, f2[0]);
+        assertEquals(90.5f, f2[1]);
+        assertEquals(90.6f, f2[2]);
+
+        // double
+        double[] d0 = new double[]{100.1};
+        double[] d1 = new double[]{100.2, 100.3};
+        double[] d2 = new double[]{100.4, 100.5, 100.6};
+        double[] dr = mRemote.doubleArray(d0, d1, d2);
+
+        assertEquals(1, dr.length);
+        assertEquals(100.1, dr[0]);
+
+        assertEquals(100.1, d1[0]);
+        assertEquals(0, d1[1], 0.0);
+
+        assertEquals(100.1, d2[0]);
+        assertEquals(100.5, d2[1]);
+        assertEquals(100.6, d2[2]);
+
+        // String
+        String[] s0 = new String[]{"s0[0]"};
+        String[] s1 = new String[]{"s1[0]", "s1[1]"};
+        String[] s2 = new String[]{"s2[0]", "s2[1]", "s2[2]"};
+        String[] sr = mRemote.stringArray(s0, s1, s2);
+
+        assertEquals(1, sr.length);
+        assertEquals("s0[0]", sr[0]);
+
+        assertEquals("s0[0]", s1[0]);
+        assertNull(s1[1]);
+
+        assertEquals("s0[0]", s2[0]);
+        assertEquals("s2[1]", s2[1]);
+        assertEquals("s2[2]", s2[2]);
+    }
+    
+    @SmallTest
+    public void testVoidSecurityException() throws Exception {
+        boolean good = false;
+        try {
+            mRemote.voidSecurityException();
+        } catch (SecurityException e) {
+            good = true;
+        }
+        assertEquals(good, true);
+    }
+    
+    @SmallTest
+    public void testIntSecurityException() throws Exception {
+        boolean good = false;
+        try {
+            mRemote.intSecurityException();
+        } catch (SecurityException e) {
+            good = true;
+        }
+        assertEquals(good, true);
+    }
+}
+
diff --git a/core/tests/coretests/src/android/os/BroadcasterTest.java b/core/tests/coretests/src/android/os/BroadcasterTest.java
new file mode 100644
index 0000000..551ea8d
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BroadcasterTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.os.Broadcaster;
+import android.os.Handler;
+import android.os.Message;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+public class BroadcasterTest extends TestCase {
+    private static final int MESSAGE_A = 23234;
+    private static final int MESSAGE_B = 3;
+    private static final int MESSAGE_C = 14;
+    private static final int MESSAGE_D = 95;
+
+    @MediumTest
+    public void test1() throws Exception {
+        /*
+        * One handler requestes one message, with a translation
+        */
+        HandlerTester tester = new HandlerTester() {
+            Handler h;
+
+            public void go() {
+                Broadcaster b = new Broadcaster();
+                h = new H();
+
+                b.request(MESSAGE_A, h, MESSAGE_B);
+
+                Message msg = new Message();
+                msg.what = MESSAGE_A;
+
+                b.broadcast(msg);
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == MESSAGE_B) {
+                    success();
+                } else {
+                    failure();
+                }
+            }
+        };
+        tester.doTest(1000);
+    }
+
+    private static class Tests2and3 extends HandlerTester {
+        Tests2and3(int n) {
+            N = n;
+        }
+
+        int N;
+        Handler mHandlers[];
+        boolean mSuccess[];
+
+        public void go() {
+            Broadcaster b = new Broadcaster();
+            mHandlers = new Handler[N];
+            mSuccess = new boolean[N];
+            for (int i = 0; i < N; i++) {
+                mHandlers[i] = new H();
+                mSuccess[i] = false;
+                b.request(MESSAGE_A, mHandlers[i], MESSAGE_B + i);
+            }
+
+            Message msg = new Message();
+            msg.what = MESSAGE_A;
+
+            b.broadcast(msg);
+        }
+
+        public void handleMessage(Message msg) {
+            int index = msg.what - MESSAGE_B;
+            if (index < 0 || index >= N) {
+                failure();
+            } else {
+                if (msg.getTarget() == mHandlers[index]) {
+                    mSuccess[index] = true;
+                }
+            }
+            boolean winner = true;
+            for (int i = 0; i < N; i++) {
+                if (!mSuccess[i]) {
+                    winner = false;
+                }
+            }
+            if (winner) {
+                success();
+            }
+        }
+    }
+
+    @MediumTest
+    public void test2() throws Exception {
+        /*
+        * 2 handlers request the same message, with different translations
+        */
+        HandlerTester tester = new Tests2and3(2);
+        tester.doTest(1000);
+    }
+
+    @MediumTest
+    public void test3() throws Exception {
+        /*
+        * 1000 handlers request the same message, with different translations
+        */
+        HandlerTester tester = new Tests2and3(10);
+        tester.doTest(1000);
+    }
+
+    @MediumTest
+    public void test4() throws Exception {
+        /*
+        * Two handlers request different messages, with translations, sending
+        * only one.  The other one should never get sent.
+        */
+        HandlerTester tester = new HandlerTester() {
+            Handler h1;
+            Handler h2;
+
+            public void go() {
+                Broadcaster b = new Broadcaster();
+                h1 = new H();
+                h2 = new H();
+
+                b.request(MESSAGE_A, h1, MESSAGE_C);
+                b.request(MESSAGE_B, h2, MESSAGE_D);
+
+                Message msg = new Message();
+                msg.what = MESSAGE_A;
+
+                b.broadcast(msg);
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == MESSAGE_C && msg.getTarget() == h1) {
+                    success();
+                } else {
+                    failure();
+                }
+            }
+        };
+        tester.doTest(1000);
+    }
+
+    @MediumTest
+    public void test5() throws Exception {
+        /*
+        * Two handlers request different messages, with translations, sending
+        * only one.  The other one should never get sent.
+        */
+        HandlerTester tester = new HandlerTester() {
+            Handler h1;
+            Handler h2;
+
+            public void go() {
+                Broadcaster b = new Broadcaster();
+                h1 = new H();
+                h2 = new H();
+
+                b.request(MESSAGE_A, h1, MESSAGE_C);
+                b.request(MESSAGE_B, h2, MESSAGE_D);
+
+                Message msg = new Message();
+                msg.what = MESSAGE_B;
+
+                b.broadcast(msg);
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == MESSAGE_D && msg.getTarget() == h2) {
+                    success();
+                } else {
+                    failure();
+                }
+            }
+        };
+        tester.doTest(1000);
+    }
+
+    @MediumTest
+    public void test6() throws Exception {
+        /*
+        * Two handlers request same message. Cancel the request for the
+        * 2nd handler, make sure the first still works.
+        */
+        HandlerTester tester = new HandlerTester() {
+            Handler h1;
+            Handler h2;
+
+            public void go() {
+                Broadcaster b = new Broadcaster();
+                h1 = new H();
+                h2 = new H();
+
+                b.request(MESSAGE_A, h1, MESSAGE_C);
+                b.request(MESSAGE_A, h2, MESSAGE_D);
+                b.cancelRequest(MESSAGE_A, h2, MESSAGE_D);
+
+                Message msg = new Message();
+                msg.what = MESSAGE_A;
+
+                b.broadcast(msg);
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == MESSAGE_C && msg.getTarget() == h1) {
+                    success();
+                } else {
+                    failure();
+                }
+            }
+        };
+        tester.doTest(1000);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/BuildTest.java b/core/tests/coretests/src/android/os/BuildTest.java
new file mode 100644
index 0000000..3758627
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BuildTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.os;
+
+import android.os.Build;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+/**
+ * Provides test cases for android.os.Build and, in turn, many of the
+ * system properties set by the build system.
+ */
+public class BuildTest extends TestCase {
+
+    private static final String TAG = "BuildTest";
+
+    /**
+     * Asserts that a String is non-null and non-empty.  If it is not,
+     * an AssertionFailedError is thrown with the given message.
+     */
+    private static void assertNotEmpty(String message, String string) {
+        //Log.i(TAG, "" + message + ": " + string);
+        assertNotNull(message, string);
+        assertFalse(message, string.equals(""));
+    }
+
+    /**
+     * Asserts that a String is non-null and non-empty.  If it is not,
+     * an AssertionFailedError is thrown.
+     */
+    private static void assertNotEmpty(String string) {
+        assertNotEmpty(null, string);
+    }
+
+    /**
+     * Asserts that all android.os.Build fields are non-empty and/or in a valid range.
+     */
+    @SmallTest
+    public void testBuildFields() throws Exception {
+        assertNotEmpty("ID", Build.ID);
+        assertNotEmpty("DISPLAY", Build.DISPLAY);
+        assertNotEmpty("PRODUCT", Build.PRODUCT);
+        assertNotEmpty("DEVICE", Build.DEVICE);
+        assertNotEmpty("BOARD", Build.BOARD);
+        assertNotEmpty("BRAND", Build.BRAND);
+        assertNotEmpty("MODEL", Build.MODEL);
+        assertNotEmpty("VERSION.INCREMENTAL", Build.VERSION.INCREMENTAL);
+        assertNotEmpty("VERSION.RELEASE", Build.VERSION.RELEASE);
+        assertNotEmpty("TYPE", Build.TYPE);
+        Assert.assertNotNull("TAGS", Build.TAGS); // TAGS is allowed to be empty.
+        assertNotEmpty("FINGERPRINT", Build.FINGERPRINT);
+        Assert.assertTrue("TIME", Build.TIME > 0);
+        assertNotEmpty("USER", Build.USER);
+        assertNotEmpty("HOST", Build.HOST);
+
+        // TODO: if any of the android.os.Build fields have additional constraints
+        // (e.g., must be a C identifier, must be a valid filename, must not contain any spaces)
+        // add tests for them.
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileObserverTest.java b/core/tests/coretests/src/android/os/FileObserverTest.java
new file mode 100644
index 0000000..ca4e0d6
--- /dev/null
+++ b/core/tests/coretests/src/android/os/FileObserverTest.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import android.os.FileObserver;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class FileObserverTest extends AndroidTestCase {
+    private Observer mObserver;
+    private File mTestFile;
+
+    private static class Observer extends FileObserver {
+        public List<Map> events = Lists.newArrayList();
+        public int totalEvents = 0;
+
+        public Observer(String path) {
+            super(path);
+        }
+
+        public void onEvent(int event, String path) {
+            synchronized (this) {
+                totalEvents++;
+                Map<String, Object> map = Maps.newHashMap();
+
+                map.put("event", event);
+                map.put("path", path);
+
+                events.add(map);
+
+                this.notifyAll();
+            }
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mTestFile = File.createTempFile(".file_observer_test", ".txt");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mTestFile != null && mTestFile.exists()) {
+            mTestFile.delete();
+        }
+    }
+
+    @LargeTest
+    public void testRun() throws Exception {
+        // make file changes and wait for them
+        assertTrue(mTestFile.exists());
+        assertNotNull(mTestFile.getParent());
+
+        mObserver = new Observer(mTestFile.getParent());
+        mObserver.startWatching();
+
+        FileOutputStream out = new FileOutputStream(mTestFile);
+        try {
+            out.write(0x20);
+            waitForEvent(); // open
+            waitForEvent(); // modify
+
+            mTestFile.delete();
+            waitForEvent(); // modify
+            waitForEvent(); // delete
+
+            mObserver.stopWatching();
+
+            // Ensure that we have seen at least 3 events.
+            assertTrue(mObserver.totalEvents > 3);
+        } finally {
+            out.close();
+        }
+    }
+
+    private void waitForEvent() {
+        synchronized (mObserver) {
+            boolean done = false;
+            while (!done) {
+                try {
+                    mObserver.wait(2000);
+                    done = true;
+                } catch (InterruptedException e) {
+                }
+            }
+
+            Iterator<Map> it = mObserver.events.iterator();
+
+            while (it.hasNext()) {
+                Map map = it.next();
+                Log.i("FileObserverTest", "event: " + getEventString((Integer)map.get("event")) + " path: " + map.get("path"));
+            }
+
+            mObserver.events.clear();
+        }
+    }
+
+    private String getEventString(int event) {
+        switch (event) {
+            case  FileObserver.ACCESS:
+                return "ACCESS";
+            case FileObserver.MODIFY:
+                return "MODIFY";
+            case FileObserver.ATTRIB:
+                return "ATTRIB";
+            case FileObserver.CLOSE_WRITE:
+                return "CLOSE_WRITE";
+            case FileObserver.CLOSE_NOWRITE:
+                return "CLOSE_NOWRITE";
+            case FileObserver.OPEN:
+                return "OPEN";
+            case FileObserver.MOVED_FROM:
+                return "MOVED_FROM";
+            case FileObserver.MOVED_TO:
+                return "MOVED_TO";
+            case FileObserver.CREATE:
+                return "CREATE";
+            case FileObserver.DELETE:
+                return "DELETE";
+            case FileObserver.DELETE_SELF:
+                return "DELETE_SELF";
+            case FileObserver.MOVE_SELF:
+                return "MOVE_SELF";
+            default:
+                return "UNKNOWN";
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
new file mode 100644
index 0000000..f12cbe1
--- /dev/null
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.FileUtils.FileStatus;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import junit.framework.Assert;
+
+public class FileUtilsTest extends AndroidTestCase {
+    private static final String TEST_DATA =
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+    private File mTestFile;
+    private File mCopyFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        File testDir = getContext().getDir("testing", Context.MODE_PRIVATE);
+        mTestFile = new File(testDir, "test.file");
+        mCopyFile = new File(testDir, "copy.file");
+        FileWriter writer = new FileWriter(mTestFile);
+        try {
+            writer.write(TEST_DATA, 0, TEST_DATA.length());
+        } finally {
+            writer.close();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mTestFile.exists()) mTestFile.delete();
+        if (mCopyFile.exists()) mCopyFile.delete();
+    }
+
+    @LargeTest
+    public void testGetFileStatus() {
+        final byte[] MAGIC = { 0xB, 0xE, 0x0, 0x5 };
+
+        try {
+            // truncate test file and write MAGIC (4 bytes) to it.
+            FileOutputStream os = new FileOutputStream(mTestFile, false);
+            os.write(MAGIC, 0, 4);
+            os.flush();
+            os.close();
+        } catch (FileNotFoundException e) {
+            Assert.fail("File was removed durning test" + e);
+        } catch (IOException e) {
+            Assert.fail("Unexpected IOException: " + e);
+        }
+        
+        Assert.assertTrue(mTestFile.exists());
+        Assert.assertTrue(FileUtils.getFileStatus(mTestFile.getPath(), null));
+        
+        FileStatus status1 = new FileStatus();
+        FileUtils.getFileStatus(mTestFile.getPath(), status1);
+        
+        Assert.assertEquals(4, status1.size);
+        
+        // Sleep for at least one second so that the modification time will be different.
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+        }
+
+        try {
+            // append so we don't change the creation time.
+            FileOutputStream os = new FileOutputStream(mTestFile, true);
+            os.write(MAGIC, 0, 4);
+            os.flush();
+            os.close();
+        } catch (FileNotFoundException e) {
+            Assert.fail("File was removed durning test" + e);
+        } catch (IOException e) {
+            Assert.fail("Unexpected IOException: " + e);
+        }
+        
+        FileStatus status2 = new FileStatus();
+        FileUtils.getFileStatus(mTestFile.getPath(), status2);
+        
+        Assert.assertEquals(8, status2.size);
+        Assert.assertTrue(status2.mtime > status1.mtime);
+        
+        mTestFile.delete();
+        
+        Assert.assertFalse(mTestFile.exists());
+        Assert.assertFalse(FileUtils.getFileStatus(mTestFile.getPath(), null));
+    }
+
+    // TODO: test setPermissions(), getPermissions()
+
+    @MediumTest
+    public void testCopyFile() throws Exception {
+        assertFalse(mCopyFile.exists());
+        FileUtils.copyFile(mTestFile, mCopyFile);
+        assertTrue(mCopyFile.exists());
+        assertEquals(TEST_DATA, FileUtils.readTextFile(mCopyFile, 0, null));
+    }
+
+    @MediumTest
+    public void testCopyToFile() throws Exception {
+        final String s = "Foo Bar";
+        assertFalse(mCopyFile.exists());
+        FileUtils.copyToFile(new ByteArrayInputStream(s.getBytes()), mCopyFile);        assertTrue(mCopyFile.exists());
+        assertEquals(s, FileUtils.readTextFile(mCopyFile, 0, null));
+    }
+
+    @MediumTest
+    public void testIsFilenameSafe() throws Exception {
+        assertTrue(FileUtils.isFilenameSafe(new File("foobar")));
+        assertTrue(FileUtils.isFilenameSafe(new File("a_b-c=d.e/0,1+23")));
+        assertFalse(FileUtils.isFilenameSafe(new File("foo*bar")));
+        assertFalse(FileUtils.isFilenameSafe(new File("foo\nbar")));
+    }
+
+    @MediumTest
+    public void testReadTextFile() throws Exception {
+        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 0, null));
+
+        assertEquals("ABCDE", FileUtils.readTextFile(mTestFile, 5, null));
+        assertEquals("ABCDE<>", FileUtils.readTextFile(mTestFile, 5, "<>"));
+        assertEquals(TEST_DATA.substring(0, 51) + "<>",
+                FileUtils.readTextFile(mTestFile, 51, "<>"));
+        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 52, "<>"));
+        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, 100, "<>"));
+
+        assertEquals("vwxyz", FileUtils.readTextFile(mTestFile, -5, null));
+        assertEquals("<>vwxyz", FileUtils.readTextFile(mTestFile, -5, "<>"));
+        assertEquals("<>" + TEST_DATA.substring(1, 52),
+                FileUtils.readTextFile(mTestFile, -51, "<>"));
+        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -52, "<>"));
+        assertEquals(TEST_DATA, FileUtils.readTextFile(mTestFile, -100, "<>"));
+    }
+
+    @MediumTest
+    public void testReadTextFileWithZeroLengthFile() throws Exception {
+        new FileOutputStream(mTestFile).close();  // Zero out the file
+        assertEquals("", FileUtils.readTextFile(mTestFile, 0, null));
+        assertEquals("", FileUtils.readTextFile(mTestFile, 1, "<>"));
+        assertEquals("", FileUtils.readTextFile(mTestFile, 10, "<>"));
+        assertEquals("", FileUtils.readTextFile(mTestFile, -1, "<>"));
+        assertEquals("", FileUtils.readTextFile(mTestFile, -10, "<>"));
+    }
+}
diff --git a/core/tests/coretests/src/android/os/HandlerTester.java b/core/tests/coretests/src/android/os/HandlerTester.java
new file mode 100644
index 0000000..a216a0b
--- /dev/null
+++ b/core/tests/coretests/src/android/os/HandlerTester.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+public abstract class HandlerTester extends Thread {
+    public abstract void go();
+    public abstract void handleMessage(Message msg);
+
+    public HandlerTester() {
+    }
+
+    public void doTest(long timeout) {
+        start();
+
+        synchronized (this) {
+            try {
+                wait(timeout);
+                quit();
+            }
+            catch (InterruptedException e) {
+            }
+        }
+
+        if (!mDone) {
+            throw new RuntimeException("test timed out");
+        }
+        if (!mSuccess) {
+            throw new RuntimeException("test failed");
+        }
+    }
+
+    public void success() {
+        mDone = true;
+        mSuccess = true;
+    }
+
+    public void failure() {
+        mDone = true;
+        mSuccess = false;
+    }
+
+    public void run() {
+        Looper.prepare();
+        mLooper = Looper.myLooper();
+        go();
+        Looper.loop();
+    }
+
+    protected class H extends Handler {
+        public void handleMessage(Message msg) {
+            synchronized (HandlerTester.this) {
+                // Call into them with our monitor locked, so they don't have
+                // to deal with other races.
+                HandlerTester.this.handleMessage(msg);
+                if (mDone) {
+                    HandlerTester.this.notify();
+                    quit();
+                }
+            }
+        }
+    }
+
+    private void quit() {
+        mLooper.quit();
+    }
+
+    private boolean mDone = false;
+    private boolean mSuccess = false;
+    private Looper mLooper;
+}
+
diff --git a/core/tests/coretests/src/android/os/HandlerThreadTest.java b/core/tests/coretests/src/android/os/HandlerThreadTest.java
new file mode 100644
index 0000000..9772aa4
--- /dev/null
+++ b/core/tests/coretests/src/android/os/HandlerThreadTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import junit.framework.TestCase;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.os.Process;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class HandlerThreadTest extends TestCase {
+    private static final int TEST_WHAT = 1;
+
+    private boolean mGotMessage = false;
+    private int mGotMessageWhat = -1;
+    private volatile boolean mDidSetup = false;
+    private volatile int mLooperTid = -1;
+    
+    @MediumTest
+    public void testHandlerThread() throws Exception {
+        HandlerThread th1 =  new HandlerThread("HandlerThreadTest") {
+            protected void onLooperPrepared() {
+                synchronized (HandlerThreadTest.this) {
+                    mDidSetup = true;
+                    mLooperTid = Process.myTid();
+                    HandlerThreadTest.this.notify();
+                }
+            }
+        };
+        
+        assertFalse(th1.isAlive());
+        assertNull(th1.getLooper());
+        
+        th1.start();
+        
+        assertTrue(th1.isAlive());
+        assertNotNull(th1.getLooper());
+       
+        // The call to getLooper() internally blocks until the looper is
+        // available, but will call onLooperPrepared() after that.  So we
+        // need to block here to wait for our onLooperPrepared() to complete
+        // and fill in the values we expect.
+        synchronized (this) {
+            while (!mDidSetup) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+        
+        // Make sure that the process was set.
+        assertNotSame(-1, mLooperTid);
+        // Make sure that the onLooperPrepared() was called on a different thread.
+        assertNotSame(Process.myTid(), mLooperTid);
+        
+        final Handler h1 = new Handler(th1.getLooper()) {
+            public void handleMessage(Message msg) {
+                assertEquals(TEST_WHAT, msg.what);
+                // Ensure that we are running on the same thread in which the looper was setup on.
+                assertEquals(mLooperTid, Process.myTid());
+                
+                mGotMessageWhat = msg.what;
+                mGotMessage = true;
+                synchronized(this) {
+                    notifyAll();
+                }
+            }
+        };
+        
+        Message msg = h1.obtainMessage(TEST_WHAT);
+        
+        synchronized (h1) {
+            // wait until we have the lock before sending the message.
+            h1.sendMessage(msg);
+            try {
+                // wait for the message to be handled
+                h1.wait();
+            } catch (InterruptedException e) {
+            }
+        }
+        
+        assertTrue(mGotMessage);
+        assertEquals(TEST_WHAT, mGotMessageWhat);
+    }
+}
diff --git a/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
new file mode 100644
index 0000000..6e50c7e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/HierarchicalStateMachineTest.java
@@ -0,0 +1,1392 @@
+/**
+ * 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.os;
+
+import junit.framework.TestCase;
+
+import android.os.Debug;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import android.util.Log;
+
+import com.android.internal.util.HierarchicalStateMachine;
+import com.android.internal.util.HierarchicalState;
+import com.android.internal.util.ProcessedMessages;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Test for HierarchicalStateMachine.
+ *
+ * @author wink@google.com (Wink Saville)
+ */
+public class HierarchicalStateMachineTest extends TestCase {
+    private static final int TEST_CMD_1 = 1;
+    private static final int TEST_CMD_2 = 2;
+    private static final int TEST_CMD_3 = 3;
+    private static final int TEST_CMD_4 = 4;
+    private static final int TEST_CMD_5 = 5;
+    private static final int TEST_CMD_6 = 6;
+
+    private static final boolean DBG = true;
+    private static final boolean WAIT_FOR_DEBUGGER = false;
+    private static final String TAG = "HierarchicalStateMachineTest";
+
+    /**
+     * Tests that ProcessedMessage works as a circular buffer.
+     */
+    class StateMachine0 extends HierarchicalStateMachine {
+        StateMachine0(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+            setProcessedMessagesSize(3);
+
+            // Setup state machine with 1 state
+            addState(mS1);
+
+            // Set the initial state
+            setInitialState(mS1);
+        }
+
+        class S1 extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_6) {
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine0 mThisSm;
+        private S1 mS1 = new S1();
+    }
+
+    @SmallTest
+    public void testStateMachine0() throws Exception {
+        if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+        StateMachine0 sm0 = new StateMachine0("sm0");
+        sm0.start();
+        if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 E");
+
+        synchronized (sm0) {
+            // Send 6 messages
+            for (int i = 1; i <= 6; i++) {
+                sm0.sendMessage(sm0.obtainMessage(i));
+            }
+
+            try {
+                // wait for the messages to be handled
+                sm0.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine0: exception while waiting " + e.getMessage());
+            }
+        }
+
+        assertTrue(sm0.getProcessedMessagesCount() == 6);
+        assertTrue(sm0.getProcessedMessagesSize() == 3);
+
+        ProcessedMessages.Info pmi;
+        pmi = sm0.getProcessedMessage(0);
+        assertEquals(TEST_CMD_4, pmi.getWhat());
+        assertEquals(sm0.mS1, pmi.getState());
+        assertEquals(sm0.mS1, pmi.getOriginalState());
+
+        pmi = sm0.getProcessedMessage(1);
+        assertEquals(TEST_CMD_5, pmi.getWhat());
+        assertEquals(sm0.mS1, pmi.getState());
+        assertEquals(sm0.mS1, pmi.getOriginalState());
+
+        pmi = sm0.getProcessedMessage(2);
+        assertEquals(TEST_CMD_6, pmi.getWhat());
+        assertEquals(sm0.mS1, pmi.getState());
+        assertEquals(sm0.mS1, pmi.getOriginalState());
+
+        if (sm0.isDbg()) Log.d(TAG, "testStateMachine0 X");
+    }
+
+    /**
+     * This tests enter/exit and transitions to the same state.
+     * The state machine has one state, it receives two messages
+     * in state mS1. With the first message it transitions to
+     * itself which causes it to be exited and reentered.
+     */
+    class StateMachine1 extends HierarchicalStateMachine {
+        StateMachine1(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup state machine with 1 state
+            addState(mS1);
+
+            // Set the initial state
+            setInitialState(mS1);
+            if (DBG) Log.d(TAG, "StateMachine1: ctor X");
+        }
+
+        class S1 extends HierarchicalState {
+            @Override protected void enter() {
+                mEnterCount++;
+            }
+
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_1) {
+                    assertEquals(1, mEnterCount);
+                    assertEquals(0, mExitCount);
+                    transitionTo(mS1);
+                } else if (message.what == TEST_CMD_2) {
+                    assertEquals(2, mEnterCount);
+                    assertEquals(1, mExitCount);
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+
+            @Override protected void exit() {
+                mExitCount++;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine1 mThisSm;
+        private S1 mS1 = new S1();
+
+        private int mEnterCount;
+        private int mExitCount;
+    }
+
+    @SmallTest
+    public void testStateMachine1() throws Exception {
+        StateMachine1 sm1 = new StateMachine1("sm1");
+        sm1.start();
+        if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 E");
+
+        synchronized (sm1) {
+            // Send two messages
+            sm1.sendMessage(sm1.obtainMessage(TEST_CMD_1));
+            sm1.sendMessage(sm1.obtainMessage(TEST_CMD_2));
+
+            try {
+                // wait for the messages to be handled
+                sm1.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine1: exception while waiting " + e.getMessage());
+            }
+        }
+
+        assertEquals(2, sm1.mEnterCount);
+        assertEquals(2, sm1.mExitCount);
+
+        assertTrue(sm1.getProcessedMessagesSize() == 2);
+
+        ProcessedMessages.Info pmi;
+        pmi = sm1.getProcessedMessage(0);
+        assertEquals(TEST_CMD_1, pmi.getWhat());
+        assertEquals(sm1.mS1, pmi.getState());
+        assertEquals(sm1.mS1, pmi.getOriginalState());
+
+        pmi = sm1.getProcessedMessage(1);
+        assertEquals(TEST_CMD_2, pmi.getWhat());
+        assertEquals(sm1.mS1, pmi.getState());
+        assertEquals(sm1.mS1, pmi.getOriginalState());
+
+        assertEquals(2, sm1.mEnterCount);
+        assertEquals(2, sm1.mExitCount);
+
+        if (sm1.isDbg()) Log.d(TAG, "testStateMachine1 X");
+    }
+
+    /**
+     * Test deferring messages and states with no parents. The state machine
+     * has two states, it receives two messages in state mS1 deferring them
+     * until what == TEST_CMD_2 and then transitions to state mS2. State
+     * mS2 then receives both of the deferred messages first TEST_CMD_1 and
+     * then TEST_CMD_2.
+     */
+    class StateMachine2 extends HierarchicalStateMachine {
+        StateMachine2(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup the hierarchy
+            addState(mS1);
+            addState(mS2);
+
+            // Set the initial state
+            setInitialState(mS1);
+            if (DBG) Log.d(TAG, "StateMachine2: ctor X");
+        }
+
+        class S1 extends HierarchicalState {
+            @Override protected void enter() {
+                mDidEnter = true;
+            }
+
+            @Override protected boolean processMessage(Message message) {
+                deferMessage(message);
+                if (message.what == TEST_CMD_2) {
+                    transitionTo(mS2);
+                }
+                return true;
+            }
+
+            @Override protected void exit() {
+                mDidExit = true;
+            }
+        }
+
+        class S2 extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_2) {
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine2 mThisSm;
+        private S1 mS1 = new S1();
+        private S2 mS2 = new S2();
+
+        private boolean mDidEnter = false;
+        private boolean mDidExit = false;
+    }
+
+    @SmallTest
+    public void testStateMachine2() throws Exception {
+        StateMachine2 sm2 = new StateMachine2("sm2");
+        sm2.start();
+        if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 E");
+
+        synchronized (sm2) {
+            // Send two messages
+            sm2.sendMessage(sm2.obtainMessage(TEST_CMD_1));
+            sm2.sendMessage(sm2.obtainMessage(TEST_CMD_2));
+
+            try {
+                // wait for the messages to be handled
+                sm2.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine2: exception while waiting " + e.getMessage());
+            }
+        }
+
+        assertTrue(sm2.getProcessedMessagesSize() == 4);
+
+        ProcessedMessages.Info pmi;
+        pmi = sm2.getProcessedMessage(0);
+        assertEquals(TEST_CMD_1, pmi.getWhat());
+        assertEquals(sm2.mS1, pmi.getState());
+
+        pmi = sm2.getProcessedMessage(1);
+        assertEquals(TEST_CMD_2, pmi.getWhat());
+        assertEquals(sm2.mS1, pmi.getState());
+
+        pmi = sm2.getProcessedMessage(2);
+        assertEquals(TEST_CMD_1, pmi.getWhat());
+        assertEquals(sm2.mS2, pmi.getState());
+
+        pmi = sm2.getProcessedMessage(3);
+        assertEquals(TEST_CMD_2, pmi.getWhat());
+        assertEquals(sm2.mS2, pmi.getState());
+
+        assertTrue(sm2.mDidEnter);
+        assertTrue(sm2.mDidExit);
+
+        if (sm2.isDbg()) Log.d(TAG, "testStateMachine2 X");
+    }
+
+    /**
+     * Test that unhandled messages in a child are handled by the parent.
+     * When TEST_CMD_2 is received.
+     */
+    class StateMachine3 extends HierarchicalStateMachine {
+        StateMachine3(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup the simplest hierarchy of two states
+            // mParentState and mChildState.
+            // (Use indentation to help visualize hierarchy)
+            addState(mParentState);
+                addState(mChildState, mParentState);
+
+            // Set the initial state will be the child
+            setInitialState(mChildState);
+            if (DBG) Log.d(TAG, "StateMachine3: ctor X");
+        }
+
+        class ParentState extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_2) {
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+        }
+
+        class ChildState extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                return false;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine3 mThisSm;
+        private ParentState mParentState = new ParentState();
+        private ChildState mChildState = new ChildState();
+    }
+
+    @SmallTest
+    public void testStateMachine3() throws Exception {
+        StateMachine3 sm3 = new StateMachine3("sm3");
+        sm3.start();
+        if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 E");
+
+        synchronized (sm3) {
+            // Send two messages
+            sm3.sendMessage(sm3.obtainMessage(TEST_CMD_1));
+            sm3.sendMessage(sm3.obtainMessage(TEST_CMD_2));
+
+            try {
+                // wait for the messages to be handled
+                sm3.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine3: exception while waiting " + e.getMessage());
+            }
+        }
+
+        assertTrue(sm3.getProcessedMessagesSize() == 2);
+
+        ProcessedMessages.Info pmi;
+        pmi = sm3.getProcessedMessage(0);
+        assertEquals(TEST_CMD_1, pmi.getWhat());
+        assertEquals(sm3.mParentState, pmi.getState());
+        assertEquals(sm3.mChildState, pmi.getOriginalState());
+
+        pmi = sm3.getProcessedMessage(1);
+        assertEquals(TEST_CMD_2, pmi.getWhat());
+        assertEquals(sm3.mParentState, pmi.getState());
+        assertEquals(sm3.mChildState, pmi.getOriginalState());
+
+        if (sm3.isDbg()) Log.d(TAG, "testStateMachine3 X");
+    }
+
+    /**
+     * Test a hierarchy of 3 states a parent and two children
+     * with transition from child 1 to child 2 and child 2
+     * lets the parent handle the messages.
+     */
+    class StateMachine4 extends HierarchicalStateMachine {
+        StateMachine4(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup a hierarchy of three states
+            // mParentState, mChildState1 & mChildState2
+            // (Use indentation to help visualize hierarchy)
+            addState(mParentState);
+                addState(mChildState1, mParentState);
+                addState(mChildState2, mParentState);
+
+            // Set the initial state will be child 1
+            setInitialState(mChildState1);
+            if (DBG) Log.d(TAG, "StateMachine4: ctor X");
+        }
+
+        class ParentState extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_2) {
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+        }
+
+        class ChildState1 extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                transitionTo(mChildState2);
+                return true;
+            }
+        }
+
+        class ChildState2 extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                return false;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine4 mThisSm;
+        private ParentState mParentState = new ParentState();
+        private ChildState1 mChildState1 = new ChildState1();
+        private ChildState2 mChildState2 = new ChildState2();
+    }
+
+    @SmallTest
+    public void testStateMachine4() throws Exception {
+        StateMachine4 sm4 = new StateMachine4("sm4");
+        sm4.start();
+        if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 E");
+
+        synchronized (sm4) {
+            // Send two messages
+            sm4.sendMessage(sm4.obtainMessage(TEST_CMD_1));
+            sm4.sendMessage(sm4.obtainMessage(TEST_CMD_2));
+
+            try {
+                // wait for the messages to be handled
+                sm4.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine4: exception while waiting " + e.getMessage());
+            }
+        }
+
+
+        assertTrue(sm4.getProcessedMessagesSize() == 2);
+
+        ProcessedMessages.Info pmi;
+        pmi = sm4.getProcessedMessage(0);
+        assertEquals(TEST_CMD_1, pmi.getWhat());
+        assertEquals(sm4.mChildState1, pmi.getState());
+        assertEquals(sm4.mChildState1, pmi.getOriginalState());
+
+        pmi = sm4.getProcessedMessage(1);
+        assertEquals(TEST_CMD_2, pmi.getWhat());
+        assertEquals(sm4.mParentState, pmi.getState());
+        assertEquals(sm4.mChildState2, pmi.getOriginalState());
+
+        if (sm4.isDbg()) Log.d(TAG, "testStateMachine4 X");
+    }
+
+    /**
+     * Test transition from one child to another of a "complex"
+     * hierarchy with two parents and multiple children.
+     */
+    class StateMachine5 extends HierarchicalStateMachine {
+        StateMachine5(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup a hierarchy with two parents and some children.
+            // (Use indentation to help visualize hierarchy)
+            addState(mParentState1);
+                addState(mChildState1, mParentState1);
+                addState(mChildState2, mParentState1);
+
+            addState(mParentState2);
+                addState(mChildState3, mParentState2);
+                addState(mChildState4, mParentState2);
+                    addState(mChildState5, mChildState4);
+
+            // Set the initial state will be the child
+            setInitialState(mChildState1);
+            if (DBG) Log.d(TAG, "StateMachine5: ctor X");
+        }
+
+        class ParentState1 extends HierarchicalState {
+            @Override protected void enter() {
+                mParentState1EnterCount += 1;
+            }
+            @Override protected boolean processMessage(Message message) {
+                return true;
+            }
+            @Override protected void exit() {
+                mParentState1ExitCount += 1;
+            }
+        }
+
+        class ChildState1 extends HierarchicalState {
+            @Override protected void enter() {
+                mChildState1EnterCount += 1;
+            }
+            @Override protected boolean processMessage(Message message) {
+                assertEquals(1, mParentState1EnterCount);
+                assertEquals(0, mParentState1ExitCount);
+                assertEquals(1, mChildState1EnterCount);
+                assertEquals(0, mChildState1ExitCount);
+                assertEquals(0, mChildState2EnterCount);
+                assertEquals(0, mChildState2ExitCount);
+                assertEquals(0, mParentState2EnterCount);
+                assertEquals(0, mParentState2ExitCount);
+                assertEquals(0, mChildState3EnterCount);
+                assertEquals(0, mChildState3ExitCount);
+                assertEquals(0, mChildState4EnterCount);
+                assertEquals(0, mChildState4ExitCount);
+                assertEquals(0, mChildState5EnterCount);
+                assertEquals(0, mChildState5ExitCount);
+
+                transitionTo(mChildState2);
+                return true;
+            }
+            @Override protected void exit() {
+                mChildState1ExitCount += 1;
+            }
+        }
+
+        class ChildState2 extends HierarchicalState {
+            @Override protected void enter() {
+                mChildState2EnterCount += 1;
+            }
+            @Override protected boolean processMessage(Message message) {
+                assertEquals(1, mParentState1EnterCount);
+                assertEquals(0, mParentState1ExitCount);
+                assertEquals(1, mChildState1EnterCount);
+                assertEquals(1, mChildState1ExitCount);
+                assertEquals(1, mChildState2EnterCount);
+                assertEquals(0, mChildState2ExitCount);
+                assertEquals(0, mParentState2EnterCount);
+                assertEquals(0, mParentState2ExitCount);
+                assertEquals(0, mChildState3EnterCount);
+                assertEquals(0, mChildState3ExitCount);
+                assertEquals(0, mChildState4EnterCount);
+                assertEquals(0, mChildState4ExitCount);
+                assertEquals(0, mChildState5EnterCount);
+                assertEquals(0, mChildState5ExitCount);
+
+                transitionTo(mChildState5);
+                return true;
+            }
+            @Override protected void exit() {
+                mChildState2ExitCount += 1;
+            }
+        }
+
+        class ParentState2 extends HierarchicalState {
+            @Override protected void enter() {
+                mParentState2EnterCount += 1;
+            }
+            @Override protected boolean processMessage(Message message) {
+                assertEquals(1, mParentState1EnterCount);
+                assertEquals(1, mParentState1ExitCount);
+                assertEquals(1, mChildState1EnterCount);
+                assertEquals(1, mChildState1ExitCount);
+                assertEquals(1, mChildState2EnterCount);
+                assertEquals(1, mChildState2ExitCount);
+                assertEquals(2, mParentState2EnterCount);
+                assertEquals(1, mParentState2ExitCount);
+                assertEquals(1, mChildState3EnterCount);
+                assertEquals(1, mChildState3ExitCount);
+                assertEquals(2, mChildState4EnterCount);
+                assertEquals(2, mChildState4ExitCount);
+                assertEquals(1, mChildState5EnterCount);
+                assertEquals(1, mChildState5ExitCount);
+
+                transitionToHaltingState();
+                return true;
+            }
+            @Override protected void exit() {
+                mParentState2ExitCount += 1;
+            }
+        }
+
+        class ChildState3 extends HierarchicalState {
+            @Override protected void enter() {
+                mChildState3EnterCount += 1;
+            }
+            @Override protected boolean processMessage(Message message) {
+                assertEquals(1, mParentState1EnterCount);
+                assertEquals(1, mParentState1ExitCount);
+                assertEquals(1, mChildState1EnterCount);
+                assertEquals(1, mChildState1ExitCount);
+                assertEquals(1, mChildState2EnterCount);
+                assertEquals(1, mChildState2ExitCount);
+                assertEquals(1, mParentState2EnterCount);
+                assertEquals(0, mParentState2ExitCount);
+                assertEquals(1, mChildState3EnterCount);
+                assertEquals(0, mChildState3ExitCount);
+                assertEquals(1, mChildState4EnterCount);
+                assertEquals(1, mChildState4ExitCount);
+                assertEquals(1, mChildState5EnterCount);
+                assertEquals(1, mChildState5ExitCount);
+
+                transitionTo(mChildState4);
+                return true;
+            }
+            @Override protected void exit() {
+                mChildState3ExitCount += 1;
+            }
+        }
+
+        class ChildState4 extends HierarchicalState {
+            @Override protected void enter() {
+                mChildState4EnterCount += 1;
+            }
+            @Override protected boolean processMessage(Message message) {
+                assertEquals(1, mParentState1EnterCount);
+                assertEquals(1, mParentState1ExitCount);
+                assertEquals(1, mChildState1EnterCount);
+                assertEquals(1, mChildState1ExitCount);
+                assertEquals(1, mChildState2EnterCount);
+                assertEquals(1, mChildState2ExitCount);
+                assertEquals(1, mParentState2EnterCount);
+                assertEquals(0, mParentState2ExitCount);
+                assertEquals(1, mChildState3EnterCount);
+                assertEquals(1, mChildState3ExitCount);
+                assertEquals(2, mChildState4EnterCount);
+                assertEquals(1, mChildState4ExitCount);
+                assertEquals(1, mChildState5EnterCount);
+                assertEquals(1, mChildState5ExitCount);
+
+                transitionTo(mParentState2);
+                return true;
+            }
+            @Override protected void exit() {
+                mChildState4ExitCount += 1;
+            }
+        }
+
+        class ChildState5 extends HierarchicalState {
+            @Override protected void enter() {
+                mChildState5EnterCount += 1;
+            }
+            @Override protected boolean processMessage(Message message) {
+                assertEquals(1, mParentState1EnterCount);
+                assertEquals(1, mParentState1ExitCount);
+                assertEquals(1, mChildState1EnterCount);
+                assertEquals(1, mChildState1ExitCount);
+                assertEquals(1, mChildState2EnterCount);
+                assertEquals(1, mChildState2ExitCount);
+                assertEquals(1, mParentState2EnterCount);
+                assertEquals(0, mParentState2ExitCount);
+                assertEquals(0, mChildState3EnterCount);
+                assertEquals(0, mChildState3ExitCount);
+                assertEquals(1, mChildState4EnterCount);
+                assertEquals(0, mChildState4ExitCount);
+                assertEquals(1, mChildState5EnterCount);
+                assertEquals(0, mChildState5ExitCount);
+
+                transitionTo(mChildState3);
+                return true;
+            }
+            @Override protected void exit() {
+                mChildState5ExitCount += 1;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine5 mThisSm;
+        private ParentState1 mParentState1 = new ParentState1();
+        private ChildState1 mChildState1 = new ChildState1();
+        private ChildState2 mChildState2 = new ChildState2();
+        private ParentState2 mParentState2 = new ParentState2();
+        private ChildState3 mChildState3 = new ChildState3();
+        private ChildState4 mChildState4 = new ChildState4();
+        private ChildState5 mChildState5 = new ChildState5();
+
+        private int mParentState1EnterCount = 0;
+        private int mParentState1ExitCount = 0;
+        private int mChildState1EnterCount = 0;
+        private int mChildState1ExitCount = 0;
+        private int mChildState2EnterCount = 0;
+        private int mChildState2ExitCount = 0;
+        private int mParentState2EnterCount = 0;
+        private int mParentState2ExitCount = 0;
+        private int mChildState3EnterCount = 0;
+        private int mChildState3ExitCount = 0;
+        private int mChildState4EnterCount = 0;
+        private int mChildState4ExitCount = 0;
+        private int mChildState5EnterCount = 0;
+        private int mChildState5ExitCount = 0;
+    }
+
+    @SmallTest
+    public void testStateMachine5() throws Exception {
+        StateMachine5 sm5 = new StateMachine5("sm5");
+        sm5.start();
+        if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 E");
+
+        synchronized (sm5) {
+            // Send 6 messages
+            sm5.sendMessage(sm5.obtainMessage(TEST_CMD_1));
+            sm5.sendMessage(sm5.obtainMessage(TEST_CMD_2));
+            sm5.sendMessage(sm5.obtainMessage(TEST_CMD_3));
+            sm5.sendMessage(sm5.obtainMessage(TEST_CMD_4));
+            sm5.sendMessage(sm5.obtainMessage(TEST_CMD_5));
+            sm5.sendMessage(sm5.obtainMessage(TEST_CMD_6));
+
+            try {
+                // wait for the messages to be handled
+                sm5.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine5: exception while waiting " + e.getMessage());
+            }
+        }
+
+
+        assertTrue(sm5.getProcessedMessagesSize() == 6);
+
+        assertEquals(1, sm5.mParentState1EnterCount);
+        assertEquals(1, sm5.mParentState1ExitCount);
+        assertEquals(1, sm5.mChildState1EnterCount);
+        assertEquals(1, sm5.mChildState1ExitCount);
+        assertEquals(1, sm5.mChildState2EnterCount);
+        assertEquals(1, sm5.mChildState2ExitCount);
+        assertEquals(2, sm5.mParentState2EnterCount);
+        assertEquals(2, sm5.mParentState2ExitCount);
+        assertEquals(1, sm5.mChildState3EnterCount);
+        assertEquals(1, sm5.mChildState3ExitCount);
+        assertEquals(2, sm5.mChildState4EnterCount);
+        assertEquals(2, sm5.mChildState4ExitCount);
+        assertEquals(1, sm5.mChildState5EnterCount);
+        assertEquals(1, sm5.mChildState5ExitCount);
+
+        ProcessedMessages.Info pmi;
+        pmi = sm5.getProcessedMessage(0);
+        assertEquals(TEST_CMD_1, pmi.getWhat());
+        assertEquals(sm5.mChildState1, pmi.getState());
+        assertEquals(sm5.mChildState1, pmi.getOriginalState());
+
+        pmi = sm5.getProcessedMessage(1);
+        assertEquals(TEST_CMD_2, pmi.getWhat());
+        assertEquals(sm5.mChildState2, pmi.getState());
+        assertEquals(sm5.mChildState2, pmi.getOriginalState());
+
+        pmi = sm5.getProcessedMessage(2);
+        assertEquals(TEST_CMD_3, pmi.getWhat());
+        assertEquals(sm5.mChildState5, pmi.getState());
+        assertEquals(sm5.mChildState5, pmi.getOriginalState());
+
+        pmi = sm5.getProcessedMessage(3);
+        assertEquals(TEST_CMD_4, pmi.getWhat());
+        assertEquals(sm5.mChildState3, pmi.getState());
+        assertEquals(sm5.mChildState3, pmi.getOriginalState());
+
+        pmi = sm5.getProcessedMessage(4);
+        assertEquals(TEST_CMD_5, pmi.getWhat());
+        assertEquals(sm5.mChildState4, pmi.getState());
+        assertEquals(sm5.mChildState4, pmi.getOriginalState());
+
+        pmi = sm5.getProcessedMessage(5);
+        assertEquals(TEST_CMD_6, pmi.getWhat());
+        assertEquals(sm5.mParentState2, pmi.getState());
+        assertEquals(sm5.mParentState2, pmi.getOriginalState());
+
+        if (sm5.isDbg()) Log.d(TAG, "testStateMachine5 X");
+    }
+
+    /**
+     * Test that the initial state enter is invoked immediately
+     * after construction and before any other messages arrive and that
+     * sendMessageDelayed works.
+     */
+    class StateMachine6 extends HierarchicalStateMachine {
+        StateMachine6(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup state machine with 1 state
+            addState(mS1);
+
+            // Set the initial state
+            setInitialState(mS1);
+            if (DBG) Log.d(TAG, "StateMachine6: ctor X");
+        }
+
+        class S1 extends HierarchicalState {
+
+            @Override protected void enter() {
+                sendMessage(obtainMessage(TEST_CMD_1));
+            }
+
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_1) {
+                    mArrivalTimeMsg1 = SystemClock.elapsedRealtime();
+                } else if (message.what == TEST_CMD_2) {
+                    mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+
+            @Override protected void exit() {
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine6 mThisSm;
+        private S1 mS1 = new S1();
+
+        private long mArrivalTimeMsg1;
+        private long mArrivalTimeMsg2;
+    }
+
+    @SmallTest
+    public void testStateMachine6() throws Exception {
+        long sentTimeMsg2;
+        final int DELAY_TIME = 250;
+        final int DELAY_FUDGE = 20;
+
+        StateMachine6 sm6 = new StateMachine6("sm6");
+        sm6.start();
+        if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 E");
+
+        synchronized (sm6) {
+            // Send a message
+            sentTimeMsg2 = SystemClock.elapsedRealtime();
+            sm6.sendMessageDelayed(sm6.obtainMessage(TEST_CMD_2), DELAY_TIME);
+
+            try {
+                // wait for the messages to be handled
+                sm6.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine6: exception while waiting " + e.getMessage());
+            }
+        }
+
+        /**
+         * TEST_CMD_1 was sent in enter and must always have been processed
+         * immediately after construction and hence the arrival time difference
+         * should always >= to the DELAY_TIME
+         */
+        long arrivalTimeDiff = sm6.mArrivalTimeMsg2 - sm6.mArrivalTimeMsg1;
+        long expectedDelay = DELAY_TIME - DELAY_FUDGE;
+        if (sm6.isDbg()) Log.d(TAG, "testStateMachine6: expect " + arrivalTimeDiff
+                                    + " >= " + expectedDelay);
+        assertTrue(arrivalTimeDiff >= expectedDelay);
+
+        if (sm6.isDbg()) Log.d(TAG, "testStateMachine6 X");
+    }
+
+    /**
+     * Test that enter is invoked immediately after exit. This validates
+     * that enter can be used to send a watch dog message for its state.
+     */
+    class StateMachine7 extends HierarchicalStateMachine {
+        private final int SM7_DELAY_TIME = 250;
+
+        StateMachine7(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup state machine with 1 state
+            addState(mS1);
+            addState(mS2);
+
+            // Set the initial state
+            setInitialState(mS1);
+            if (DBG) Log.d(TAG, "StateMachine7: ctor X");
+        }
+
+        class S1 extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                transitionTo(mS2);
+                return true;
+            }
+            @Override protected void exit() {
+                sendMessage(obtainMessage(TEST_CMD_2));
+            }
+        }
+
+        class S2 extends HierarchicalState {
+
+            @Override protected void enter() {
+                // Send a delayed message as a watch dog
+                sendMessageDelayed(obtainMessage(TEST_CMD_3), SM7_DELAY_TIME);
+            }
+
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_2) {
+                    mMsgCount += 1;
+                    mArrivalTimeMsg2 = SystemClock.elapsedRealtime();
+                } else if (message.what == TEST_CMD_3) {
+                    mMsgCount += 1;
+                    mArrivalTimeMsg3 = SystemClock.elapsedRealtime();
+                }
+
+                if (mMsgCount == 2) {
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+
+            @Override protected void exit() {
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachine7 mThisSm;
+        private S1 mS1 = new S1();
+        private S2 mS2 = new S2();
+
+        private int mMsgCount = 0;
+        private long mArrivalTimeMsg2;
+        private long mArrivalTimeMsg3;
+    }
+
+    @SmallTest
+    public void testStateMachine7() throws Exception {
+        long sentTimeMsg2;
+        final int SM7_DELAY_FUDGE = 20;
+
+        StateMachine7 sm7 = new StateMachine7("sm7");
+        sm7.start();
+        if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 E");
+
+        synchronized (sm7) {
+            // Send a message
+            sentTimeMsg2 = SystemClock.elapsedRealtime();
+            sm7.sendMessage(sm7.obtainMessage(TEST_CMD_1));
+
+            try {
+                // wait for the messages to be handled
+                sm7.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachine7: exception while waiting " + e.getMessage());
+            }
+        }
+
+        /**
+         * TEST_CMD_3 was sent in S2.enter with a delay and must always have been
+         * processed immediately after S1.exit. Since S1.exit sent TEST_CMD_2
+         * without a delay the arrival time difference should always >= to SM7_DELAY_TIME.
+         */
+        long arrivalTimeDiff = sm7.mArrivalTimeMsg3 - sm7.mArrivalTimeMsg2;
+        long expectedDelay = sm7.SM7_DELAY_TIME - SM7_DELAY_FUDGE;
+        if (sm7.isDbg()) Log.d(TAG, "testStateMachine7: expect " + arrivalTimeDiff
+                                    + " >= " + expectedDelay);
+        assertTrue(arrivalTimeDiff >= expectedDelay);
+
+        if (sm7.isDbg()) Log.d(TAG, "testStateMachine7 X");
+    }
+
+    /**
+     * Test unhandledMessage.
+     */
+    class StateMachineUnhandledMessage extends HierarchicalStateMachine {
+        StateMachineUnhandledMessage(String name) {
+            super(name);
+            mThisSm = this;
+            setDbg(DBG);
+
+            // Setup state machine with 1 state
+            addState(mS1);
+
+            // Set the initial state
+            setInitialState(mS1);
+        }
+
+        @Override protected void unhandledMessage(Message message) {
+            mUnhandledMessageCount += 1;
+        }
+
+        class S1 extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_2) {
+                    transitionToHaltingState();
+                }
+                return false;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            synchronized (mThisSm) {
+                mThisSm.notifyAll();
+            }
+        }
+
+        private StateMachineUnhandledMessage mThisSm;
+        private int mUnhandledMessageCount;
+        private S1 mS1 = new S1();
+    }
+
+    @SmallTest
+    public void testStateMachineUnhandledMessage() throws Exception {
+
+        StateMachineUnhandledMessage sm = new StateMachineUnhandledMessage("sm");
+        sm.start();
+        if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage E");
+
+        synchronized (sm) {
+            // Send 2 messages
+            for (int i = 1; i <= 2; i++) {
+                sm.sendMessage(sm.obtainMessage(i));
+            }
+
+            try {
+                // wait for the messages to be handled
+                sm.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachineUnhandledMessage: exception while waiting "
+                        + e.getMessage());
+            }
+        }
+
+        assertTrue(sm.getProcessedMessagesCount() == 2);
+        assertEquals(2, sm.mUnhandledMessageCount);
+
+        if (sm.isDbg()) Log.d(TAG, "testStateMachineUnhandledMessage X");
+    }
+
+    /**
+     * Test state machines sharing the same thread/looper. Multiple instances
+     * of the same state machine will be created. They will all share the
+     * same thread and thus each can update <code>sharedCounter</code> which
+     * will be used to notify testStateMachineSharedThread that the test is
+     * complete.
+     */
+    class StateMachineSharedThread extends HierarchicalStateMachine {
+        StateMachineSharedThread(Looper looper, String name, int maxCount) {
+            super(looper, name);
+            mMaxCount = maxCount;
+            setDbg(DBG);
+
+            // Setup state machine with 1 state
+            addState(mS1);
+
+            // Set the initial state
+            setInitialState(mS1);
+        }
+
+        class S1 extends HierarchicalState {
+            @Override protected boolean processMessage(Message message) {
+                if (message.what == TEST_CMD_4) {
+                    transitionToHaltingState();
+                }
+                return true;
+            }
+        }
+
+        @Override
+        protected void halting() {
+            // Update the shared counter, which is OK since all state
+            // machines are using the same thread.
+            sharedCounter += 1;
+            if (sharedCounter == mMaxCount) {
+                synchronized (waitObject) {
+                    waitObject.notifyAll();
+                }
+            }
+        }
+
+        private int mMaxCount;
+        private S1 mS1 = new S1();
+    }
+    private static int sharedCounter = 0;
+    private static Object waitObject = new Object();
+
+    @SmallTest
+    public void testStateMachineSharedThread() throws Exception {
+        if (DBG) Log.d(TAG, "testStateMachineSharedThread E");
+
+        // Create and start the handler thread
+        HandlerThread smThread = new HandlerThread("testStateMachineSharedThread");
+        smThread.start();
+
+        // Create the state machines
+        StateMachineSharedThread sms[] = new StateMachineSharedThread[10];
+        for (int i = 0; i < sms.length; i++) {
+            sms[i] = new StateMachineSharedThread(smThread.getLooper(), "sm", sms.length);
+            sms[i].start();
+        }
+
+        synchronized (waitObject) {
+            // Send messages to each of the state machines
+            for (StateMachineSharedThread sm : sms) {
+                for (int i = 1; i <= 4; i++) {
+                    sm.sendMessage(sm.obtainMessage(i));
+                }
+            }
+
+            // Wait for the last state machine to notify its done
+            try {
+                waitObject.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testStateMachineSharedThread: exception while waiting "
+                        + e.getMessage());
+            }
+        }
+
+        for (StateMachineSharedThread sm : sms) {
+            assertTrue(sm.getProcessedMessagesCount() == 4);
+            for (int i = 0; i < sm.getProcessedMessagesCount(); i++) {
+                ProcessedMessages.Info pmi = sm.getProcessedMessage(i);
+                assertEquals(i+1, pmi.getWhat());
+                assertEquals(sm.mS1, pmi.getState());
+                assertEquals(sm.mS1, pmi.getOriginalState());
+            }
+        }
+
+        if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
+    }
+
+    @SmallTest
+    public void testHsm1() throws Exception {
+        if (DBG) Log.d(TAG, "testHsm1 E");
+
+        Hsm1 sm = Hsm1.makeHsm1();
+
+        // Send messages
+        sm.sendMessage(sm.obtainMessage(Hsm1.CMD_1));
+        sm.sendMessage(sm.obtainMessage(Hsm1.CMD_2));
+
+        synchronized (sm) {
+            // Wait for the last state machine to notify its done
+            try {
+                sm.wait();
+            } catch (InterruptedException e) {
+                Log.e(TAG, "testHsm1: exception while waiting " + e.getMessage());
+            }
+        }
+
+        assertEquals(7, sm.getProcessedMessagesCount());
+        ProcessedMessages.Info pmi = sm.getProcessedMessage(0);
+        assertEquals(Hsm1.CMD_1, pmi.getWhat());
+        assertEquals(sm.mS1, pmi.getState());
+        assertEquals(sm.mS1, pmi.getOriginalState());
+
+        pmi = sm.getProcessedMessage(1);
+        assertEquals(Hsm1.CMD_2, pmi.getWhat());
+        assertEquals(sm.mP1, pmi.getState());
+        assertEquals(sm.mS1, pmi.getOriginalState());
+
+        pmi = sm.getProcessedMessage(2);
+        assertEquals(Hsm1.CMD_2, pmi.getWhat());
+        assertEquals(sm.mS2, pmi.getState());
+        assertEquals(sm.mS2, pmi.getOriginalState());
+
+        pmi = sm.getProcessedMessage(3);
+        assertEquals(Hsm1.CMD_3, pmi.getWhat());
+        assertEquals(sm.mS2, pmi.getState());
+        assertEquals(sm.mS2, pmi.getOriginalState());
+
+        pmi = sm.getProcessedMessage(4);
+        assertEquals(Hsm1.CMD_3, pmi.getWhat());
+        assertEquals(sm.mP2, pmi.getState());
+        assertEquals(sm.mP2, pmi.getOriginalState());
+
+        pmi = sm.getProcessedMessage(5);
+        assertEquals(Hsm1.CMD_4, pmi.getWhat());
+        assertEquals(sm.mP2, pmi.getState());
+        assertEquals(sm.mP2, pmi.getOriginalState());
+
+        pmi = sm.getProcessedMessage(6);
+        assertEquals(Hsm1.CMD_5, pmi.getWhat());
+        assertEquals(sm.mP2, pmi.getState());
+        assertEquals(sm.mP2, pmi.getOriginalState());
+
+        if (DBG) Log.d(TAG, "testStateMachineSharedThread X");
+    }
+}
+
+class Hsm1 extends HierarchicalStateMachine {
+    private static final String TAG = "hsm1";
+
+    public static final int CMD_1 = 1;
+    public static final int CMD_2 = 2;
+    public static final int CMD_3 = 3;
+    public static final int CMD_4 = 4;
+    public static final int CMD_5 = 5;
+
+    public static Hsm1 makeHsm1() {
+        Log.d(TAG, "makeHsm1 E");
+        Hsm1 sm = new Hsm1("hsm1");
+        sm.start();
+        Log.d(TAG, "makeHsm1 X");
+        return sm;
+    }
+
+    Hsm1(String name) {
+        super(name);
+        Log.d(TAG, "ctor E");
+
+        // Add states, use indentation to show hierarchy
+        addState(mP1);
+            addState(mS1, mP1);
+            addState(mS2, mP1);
+        addState(mP2);
+
+        // Set the initial state
+        setInitialState(mS1);
+        Log.d(TAG, "ctor X");
+    }
+
+    class P1 extends HierarchicalState {
+        @Override protected void enter() {
+            Log.d(TAG, "P1.enter");
+        }
+        @Override protected boolean processMessage(Message message) {
+            boolean retVal;
+            Log.d(TAG, "P1.processMessage what=" + message.what);
+            switch(message.what) {
+            case CMD_2:
+                // CMD_2 will arrive in mS2 before CMD_3
+                sendMessage(obtainMessage(CMD_3));
+                deferMessage(message);
+                transitionTo(mS2);
+                retVal = true;
+                break;
+            default:
+                // Any message we don't understand in this state invokes unhandledMessage
+                retVal = false;
+                break;
+            }
+            return retVal;
+        }
+        @Override protected void exit() {
+            Log.d(TAG, "P1.exit");
+        }
+    }
+
+    class S1 extends HierarchicalState {
+        @Override protected void enter() {
+            Log.d(TAG, "S1.enter");
+        }
+        @Override protected boolean processMessage(Message message) {
+            Log.d(TAG, "S1.processMessage what=" + message.what);
+            if (message.what == CMD_1) {
+                // Transition to ourself to show that enter/exit is called
+                transitionTo(mS1);
+                return true;
+            } else {
+                // Let parent process all other messages
+                return false;
+            }
+        }
+        @Override protected void exit() {
+            Log.d(TAG, "S1.exit");
+        }
+    }
+
+    class S2 extends HierarchicalState {
+        @Override protected void enter() {
+            Log.d(TAG, "S2.enter");
+        }
+        @Override protected boolean processMessage(Message message) {
+            boolean retVal;
+            Log.d(TAG, "S2.processMessage what=" + message.what);
+            switch(message.what) {
+            case(CMD_2):
+                sendMessage(obtainMessage(CMD_4));
+                retVal = true;
+                break;
+            case(CMD_3):
+                deferMessage(message);
+                transitionTo(mP2);
+                retVal = true;
+                break;
+            default:
+                retVal = false;
+                break;
+            }
+            return retVal;
+        }
+        @Override protected void exit() {
+            Log.d(TAG, "S2.exit");
+        }
+    }
+
+    class P2 extends HierarchicalState {
+        @Override protected void enter() {
+            Log.d(TAG, "P2.enter");
+            sendMessage(obtainMessage(CMD_5));
+        }
+        @Override protected boolean processMessage(Message message) {
+            Log.d(TAG, "P2.processMessage what=" + message.what);
+            switch(message.what) {
+            case(CMD_3):
+                break;
+            case(CMD_4):
+                break;
+            case(CMD_5):
+                transitionToHaltingState();
+                break;
+            }
+            return true;
+        }
+        @Override protected void exit() {
+            Log.d(TAG, "P2.exit");
+        }
+    }
+
+    @Override
+    protected void halting() {
+        Log.d(TAG, "halting");
+        synchronized (this) {
+            this.notifyAll();
+        }
+    }
+
+    P1 mP1 = new P1();
+    S1 mS1 = new S1();
+    S2 mS2 = new S2();
+    P2 mP2 = new P2();
+}
diff --git a/core/tests/coretests/src/android/os/IAidlTest.aidl b/core/tests/coretests/src/android/os/IAidlTest.aidl
new file mode 100644
index 0000000..a09022e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IAidlTest.aidl
@@ -0,0 +1,47 @@
+/* //device/apps/AndroidTests/src/com.android.unit_tests/IAidlTest.aidl
+**
+** Copyright 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.
+*/
+
+package android.os;
+
+import android.os.AidlTest;
+
+interface IAidlTest {
+    int intMethod(int a);
+
+    AidlTest.TestParcelable parcelableIn(in AidlTest.TestParcelable p);
+    AidlTest.TestParcelable parcelableOut(out AidlTest.TestParcelable p);
+    AidlTest.TestParcelable parcelableInOut(inout AidlTest.TestParcelable p);
+
+    AidlTest.TestParcelable listParcelableLonger(
+                    inout List<AidlTest.TestParcelable> list, int index);
+    int listParcelableShorter(
+                    inout List<AidlTest.TestParcelable> list, int index);
+
+    boolean[] booleanArray(in boolean[] a0, out boolean[] a1, inout boolean[] a2);
+    char[] charArray(in char[] a0, out char[] a1, inout char[] a2);
+    int[] intArray(in int[] a0, out int[] a1, inout int[] a2);
+    long[] longArray(in long[] a0, out long[] a1, inout long[] a2);
+    float[] floatArray(in float[] a0, out float[] a1, inout float[] a2);
+    double[] doubleArray(in double[] a0, out double[] a1, inout double[] a2);
+    String[] stringArray(in String[] a0, out String[] a1, inout String[] a2);
+    AidlTest.TestParcelable[] parcelableArray(in AidlTest.TestParcelable[] a0,
+                                          out AidlTest.TestParcelable[] a1,
+                                          inout AidlTest.TestParcelable[] a2);
+                                          
+    void voidSecurityException();
+    int intSecurityException();
+}
diff --git a/core/tests/coretests/src/android/os/IdleHandlerTest.java b/core/tests/coretests/src/android/os/IdleHandlerTest.java
new file mode 100644
index 0000000..6c0a862
--- /dev/null
+++ b/core/tests/coretests/src/android/os/IdleHandlerTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue.IdleHandler;
+import android.test.suitebuilder.annotation.MediumTest;
+import junit.framework.TestCase;
+
+public class IdleHandlerTest extends TestCase {
+
+    private static class BaseTestHandler extends TestHandlerThread {
+        Handler mHandler;
+
+        public BaseTestHandler() {
+        }
+
+        public void go() {
+            mHandler = new Handler() {
+                public void handleMessage(Message msg) {
+                    BaseTestHandler.this.handleMessage(msg);
+                }
+            };
+        }
+
+        public void addIdleHandler() {
+            Looper.myQueue().addIdleHandler(new IdleHandler() {
+                public boolean queueIdle() {
+                    return BaseTestHandler.this.queueIdle();
+                }
+            });
+        }
+
+        public void handleMessage(Message msg) {
+        }
+
+        public boolean queueIdle() {
+            return false;
+        }
+    }
+
+    @MediumTest
+    public void testOneShotFirst() throws Exception {
+        TestHandlerThread tester = new BaseTestHandler() {
+            int mCount;
+
+            public void go() {
+                super.go();
+                mCount = 0;
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(0), 100);
+                addIdleHandler();
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == 0) {
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+                } else if (msg.what == 1) {
+                    if (mCount == 1) {
+                        success();
+                    } else {
+                        failure(new RuntimeException(
+                                "Idle handler called " + mCount + " times"));
+                    }
+                }
+            }
+
+            public boolean queueIdle() {
+                mCount++;
+                return false;
+            }
+        };
+
+        tester.doTest(1000);
+    }
+
+    @MediumTest
+    public void testOneShotLater() throws Exception {
+        TestHandlerThread tester = new BaseTestHandler() {
+            int mCount;
+
+            public void go() {
+                super.go();
+                mCount = 0;
+                mHandler.sendMessage(mHandler.obtainMessage(0));
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == 0) {
+                    addIdleHandler();
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+                } else if (msg.what == 1) {
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(2), 100);
+                } else if (msg.what == 2) {
+                    if (mCount == 1) {
+                        success();
+                    } else {
+                        failure(new RuntimeException(
+                                "Idle handler called " + mCount + " times"));
+                    }
+                }
+            }
+
+            public boolean queueIdle() {
+                mCount++;
+                return false;
+            }
+        };
+
+        tester.doTest(1000);
+    }
+
+
+    @MediumTest
+    public void testRepeatedFirst() throws Exception {
+        TestHandlerThread tester = new BaseTestHandler() {
+            int mCount;
+
+            public void go() {
+                super.go();
+                mCount = 0;
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(0), 100);
+                addIdleHandler();
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == 0) {
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+                } else if (msg.what == 1) {
+                    if (mCount == 2) {
+                        success();
+                    } else {
+                        failure(new RuntimeException(
+                                "Idle handler called " + mCount + " times"));
+                    }
+                }
+            }
+
+            public boolean queueIdle() {
+                mCount++;
+                return true;
+            }
+        };
+
+        tester.doTest(1000);
+    }
+
+    @MediumTest
+    public void testRepeatedLater() throws Exception {
+        TestHandlerThread tester = new BaseTestHandler() {
+            int mCount;
+
+            public void go() {
+                super.go();
+                mCount = 0;
+                mHandler.sendMessage(mHandler.obtainMessage(0));
+            }
+
+            public void handleMessage(Message msg) {
+                if (msg.what == 0) {
+                    addIdleHandler();
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(1), 100);
+                } else if (msg.what == 1) {
+                    mHandler.sendMessageDelayed(mHandler.obtainMessage(2), 100);
+                } else if (msg.what == 2) {
+                    if (mCount == 2) {
+                        success();
+                    } else {
+                        failure(new RuntimeException(
+                                "Idle handler called " + mCount + " times"));
+                    }
+                }
+            }
+
+            public boolean queueIdle() {
+                mCount++;
+                return true;
+            }
+        };
+
+        tester.doTest(1000);
+    }
+}
+
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
new file mode 100644
index 0000000..411bdaa
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -0,0 +1,427 @@
+/*
+ * 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.os;
+
+import android.os.MemoryFile;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MemoryFileTest extends AndroidTestCase {
+
+    private void compareBuffers(byte[] buffer1, byte[] buffer2, int length) throws Exception {
+        for (int i = 0; i < length; i++) {
+            if (buffer1[i] != buffer2[i]) {
+                throw new Exception("readBytes did not read back what writeBytes wrote");
+            }
+        }
+    }
+
+    /**
+     * Keep allocating new files till the system purges them.
+     */
+    @MediumTest
+    public void testPurge() throws Exception {
+        List<MemoryFile> files = new ArrayList<MemoryFile>();
+        while (true) {
+            MemoryFile newFile = new MemoryFile("MemoryFileTest", 1000000);
+            newFile.allowPurging(true);
+            newFile.writeBytes(testString, 0, 0, testString.length);
+            files.add(newFile);
+            for (MemoryFile file : files) {
+                try {
+                    file.readBytes(testString, 0, 0, testString.length);
+                } catch (IOException e) {
+                    // Expected
+                    for (MemoryFile fileToClose : files) {
+                        fileToClose.close();
+                    }
+                    return;
+                }
+            }
+        }
+    }
+
+    @SmallTest
+    public void testRun() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+
+        byte[] buffer = new byte[testString.length];
+
+        // check low level accessors
+        file.writeBytes(testString, 0, 2000, testString.length);
+        file.readBytes(buffer, 2000, 0, testString.length);
+        compareBuffers(testString, buffer, testString.length);
+
+        // check streams
+        buffer = new byte[testString.length];
+
+        OutputStream os = file.getOutputStream();
+        os.write(testString);
+
+        InputStream is = file.getInputStream();
+        is.mark(testString.length);
+        is.read(buffer);
+        compareBuffers(testString, buffer, testString.length);
+
+        // test mark/reset
+        buffer = new byte[testString.length];
+        is.reset();
+        is.read(buffer);
+        compareBuffers(testString, buffer, testString.length);
+
+        file.close();
+    }
+
+    // Tests for the IndexOutOfBoundsException cases in read().
+
+    private void readIndexOutOfBoundsException(int offset, int count, String msg)
+            throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+        try {
+            file.writeBytes(testString, 0, 0, testString.length);
+            InputStream is = file.getInputStream();
+            byte[] buffer = new byte[testString.length + 10];
+            try {
+                is.read(buffer, offset, count);
+                fail(msg);
+            } catch (IndexOutOfBoundsException ex) {
+                // this is what should happen
+            } finally {
+                is.close();
+            }
+        } finally {
+            file.close();
+        }
+    }
+
+    @SmallTest
+    public void testReadNegativeOffset() throws Exception {
+        readIndexOutOfBoundsException(-1, 5,
+                "read() with negative offset should throw IndexOutOfBoundsException");
+    }
+
+    @SmallTest
+    public void testReadNegativeCount() throws Exception {
+        readIndexOutOfBoundsException(5, -1,
+                "read() with negative length should throw IndexOutOfBoundsException");
+    }
+
+    @SmallTest
+    public void testReadOffsetOverflow() throws Exception {
+        readIndexOutOfBoundsException(testString.length + 10, 5,
+                "read() with offset outside buffer should throw IndexOutOfBoundsException");
+    }
+
+    @SmallTest
+    public void testReadOffsetCountOverflow() throws Exception {
+        readIndexOutOfBoundsException(testString.length, 11,
+                "read() with offset + count outside buffer should throw IndexOutOfBoundsException");
+    }
+
+    // Test behavior of read() at end of file
+    @SmallTest
+    public void testReadEOF() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+        try {
+            file.writeBytes(testString, 0, 0, testString.length);
+            InputStream is = file.getInputStream();
+            try {
+                byte[] buffer = new byte[testString.length + 10];
+                // read() with count larger than data should succeed, and return # of bytes read
+                assertEquals(testString.length, is.read(buffer));
+                compareBuffers(testString, buffer, testString.length);
+                // Read at EOF should return -1
+                assertEquals(-1, is.read());
+            } finally {
+                is.close();
+            }
+        } finally {
+            file.close();
+        }
+    }
+
+    // Tests that close() is idempotent
+    @SmallTest
+    public void testCloseClose() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        byte[] data = new byte[512];
+        file.writeBytes(data, 0, 0, 128);
+        file.close();
+        file.close();
+    }
+
+    // Tests that we can't read from a closed memory file
+    @SmallTest
+    public void testCloseRead() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        file.close();
+
+        try {
+            byte[] data = new byte[512];
+            assertEquals(128, file.readBytes(data, 0, 0, 128));
+            fail("readBytes() after close() did not throw IOException.");
+        } catch (IOException e) {
+            // this is what should happen
+        }
+    }
+
+    // Tests that we can't write to a closed memory file
+    @SmallTest
+    public void testCloseWrite() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        file.close();
+
+        try {
+            byte[] data = new byte[512];
+            file.writeBytes(data, 0, 0, 128);
+            fail("writeBytes() after close() did not throw IOException.");
+        } catch (IOException e) {
+            // this is what should happen
+        }
+    }
+
+    // Tests that we can't call allowPurging() after close()
+    @SmallTest
+    public void testCloseAllowPurging() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        byte[] data = new byte[512];
+        file.writeBytes(data, 0, 0, 128);
+        file.close();
+
+        try {
+            file.allowPurging(true);
+            fail("allowPurging() after close() did not throw IOException.");
+        } catch (IOException e) {
+            // this is what should happen
+        }
+    }
+
+    // Tests that we don't leak file descriptors or mmap areas
+    @LargeTest
+    public void testCloseLeak() throws Exception {
+        // open enough memory files that we should run out of
+        // file descriptors or address space if we leak either.
+        for (int i = 0; i < 1025; i++) {
+            MemoryFile file = new MemoryFile("MemoryFileTest", 5000000);
+            file.writeBytes(testString, 0, 0, testString.length);
+            file.close();
+        }
+    }
+
+    @SmallTest
+    public void testIsMemoryFile() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        FileDescriptor fd = file.getFileDescriptor();
+        assertNotNull(fd);
+        assertTrue(fd.valid());
+        assertTrue(MemoryFile.isMemoryFile(fd));
+        file.close();
+
+        assertFalse(MemoryFile.isMemoryFile(FileDescriptor.in));
+        assertFalse(MemoryFile.isMemoryFile(FileDescriptor.out));
+        assertFalse(MemoryFile.isMemoryFile(FileDescriptor.err));
+
+        File tempFile = File.createTempFile("MemoryFileTest",".tmp", getContext().getFilesDir());
+        assertNotNull(file);
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(tempFile);
+            FileDescriptor fileFd = out.getFD();
+            assertNotNull(fileFd);
+            assertFalse(MemoryFile.isMemoryFile(fileFd));
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+            tempFile.delete();
+        }
+    }
+
+    @SmallTest
+    public void testFileDescriptor() throws Exception {
+        MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
+        MemoryFile ref = new MemoryFile(file.getFileDescriptor(), file.length(), "r");
+        byte[] buffer;
+
+        // write to original, read from reference
+        file.writeBytes(testString, 0, 2000, testString.length);
+        buffer = new byte[testString.length];
+        ref.readBytes(buffer, 2000, 0, testString.length);
+        compareBuffers(testString, buffer, testString.length);
+
+        file.close();
+        ref.close();  // Doesn't actually do anything, since the file descriptor is not dup(2):ed
+    }
+
+    private static final byte[] testString = new byte[] {
+        3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 2, 3, 8, 4, 6, 2, 6, 4, 3, 3, 8, 3, 2, 7, 9, 5, 0, 2, 8, 8, 4, 1, 9, 7, 1, 6, 9, 3, 9, 9, 3, 7, 5, 1, 0, 5, 8, 2, 0, 9, 7, 4, 9, 4, 4, 5, 9, 2, 3, 0, 7, 8, 1, 6, 4,
+        0, 6, 2, 8, 6, 2, 0, 8, 9, 9, 8, 6, 2, 8, 0, 3, 4, 8, 2, 5, 3, 4, 2, 1, 1, 7, 0, 6, 7, 9, 8, 2, 1, 4, 8, 0, 8, 6, 5, 1, 3, 2, 8, 2, 3, 0, 6, 6, 4, 7, 0, 9, 3, 8, 4, 4, 6, 0, 9, 5, 5, 0, 5, 8, 2, 2, 3, 1, 7, 2,
+        5, 3, 5, 9, 4, 0, 8, 1, 2, 8, 4, 8, 1, 1, 1, 7, 4, 5, 0, 2, 8, 4, 1, 0, 2, 7, 0, 1, 9, 3, 8, 5, 2, 1, 1, 0, 5, 5, 5, 9, 6, 4, 4, 6, 2, 2, 9, 4, 8, 9, 5, 4, 9, 3, 0, 3, 8, 1, 9, 6, 4, 4, 2, 8, 8, 1, 0, 9, 7, 5,
+        6, 6, 5, 9, 3, 3, 4, 4, 6, 1, 2, 8, 4, 7, 5, 6, 4, 8, 2, 3, 3, 7, 8, 6, 7, 8, 3, 1, 6, 5, 2, 7, 1, 2, 0, 1, 9, 0, 9, 1, 4, 5, 6, 4, 8, 5, 6, 6, 9, 2, 3, 4, 6, 0, 3, 4, 8, 6, 1, 0, 4, 5, 4, 3, 2, 6, 6, 4, 8, 2,
+        1, 3, 3, 9, 3, 6, 0, 7, 2, 6, 0, 2, 4, 9, 1, 4, 1, 2, 7, 3, 7, 2, 4, 5, 8, 7, 0, 0, 6, 6, 0, 6, 3, 1, 5, 5, 8, 8, 1, 7, 4, 8, 8, 1, 5, 2, 0, 9, 2, 0, 9, 6, 2, 8, 2, 9, 2, 5, 4, 0, 9, 1, 7, 1, 5, 3, 6, 4, 3, 6,
+        7, 8, 9, 2, 5, 9, 0, 3, 6, 0, 0, 1, 1, 3, 3, 0, 5, 3, 0, 5, 4, 8, 8, 2, 0, 4, 6, 6, 5, 2, 1, 3, 8, 4, 1, 4, 6, 9, 5, 1, 9, 4, 1, 5, 1, 1, 6, 0, 9, 4, 3, 3, 0, 5, 7, 2, 7, 0, 3, 6, 5, 7, 5, 9, 5, 9, 1, 9, 5, 3,
+        0, 9, 2, 1, 8, 6, 1, 1, 7, 3, 8, 1, 9, 3, 2, 6, 1, 1, 7, 9, 3, 1, 0, 5, 1, 1, 8, 5, 4, 8, 0, 7, 4, 4, 6, 2, 3, 7, 9, 9, 6, 2, 7, 4, 9, 5, 6, 7, 3, 5, 1, 8, 8, 5, 7, 5, 2, 7, 2, 4, 8, 9, 1, 2, 2, 7, 9, 3, 8, 1,
+        8, 3, 0, 1, 1, 9, 4, 9, 1, 2, 9, 8, 3, 3, 6, 7, 3, 3, 6, 2, 4, 4, 0, 6, 5, 6, 6, 4, 3, 0, 8, 6, 0, 2, 1, 3, 9, 4, 9, 4, 6, 3, 9, 5, 2, 2, 4, 7, 3, 7, 1, 9, 0, 7, 0, 2, 1, 7, 9, 8, 6, 0, 9, 4, 3, 7, 0, 2, 7, 7,
+        0, 5, 3, 9, 2, 1, 7, 1, 7, 6, 2, 9, 3, 1, 7, 6, 7, 5, 2, 3, 8, 4, 6, 7, 4, 8, 1, 8, 4, 6, 7, 6, 6, 9, 4, 0, 5, 1, 3, 2, 0, 0, 0, 5, 6, 8, 1, 2, 7, 1, 4, 5, 2, 6, 3, 5, 6, 0, 8, 2, 7, 7, 8, 5, 7, 7, 1, 3, 4, 2,
+        7, 5, 7, 7, 8, 9, 6, 0, 9, 1, 7, 3, 6, 3, 7, 1, 7, 8, 7, 2, 1, 4, 6, 8, 4, 4, 0, 9, 0, 1, 2, 2, 4, 9, 5, 3, 4, 3, 0, 1, 4, 6, 5, 4, 9, 5, 8, 5, 3, 7, 1, 0, 5, 0, 7, 9, 2, 2, 7, 9, 6, 8, 9, 2, 5, 8, 9, 2, 3, 5,
+        4, 2, 0, 1, 9, 9, 5, 6, 1, 1, 2, 1, 2, 9, 0, 2, 1, 9, 6, 0, 8, 6, 4, 0, 3, 4, 4, 1, 8, 1, 5, 9, 8, 1, 3, 6, 2, 9, 7, 7, 4, 7, 7, 1, 3, 0, 9, 9, 6, 0, 5, 1, 8, 7, 0, 7, 2, 1, 1, 3, 4, 9, 9, 9, 9, 9, 9, 8, 3, 7,
+        2, 9, 7, 8, 0, 4, 9, 9, 5, 1, 0, 5, 9, 7, 3, 1, 7, 3, 2, 8, 1, 6, 0, 9, 6, 3, 1, 8, 5, 9, 5, 0, 2, 4, 4, 5, 9, 4, 5, 5, 3, 4, 6, 9, 0, 8, 3, 0, 2, 6, 4, 2, 5, 2, 2, 3, 0, 8, 2, 5, 3, 3, 4, 4, 6, 8, 5, 0, 3, 5,
+        2, 6, 1, 9, 3, 1, 1, 8, 8, 1, 7, 1, 0, 1, 0, 0, 0, 3, 1, 3, 7, 8, 3, 8, 7, 5, 2, 8, 8, 6, 5, 8, 7, 5, 3, 3, 2, 0, 8, 3, 8, 1, 4, 2, 0, 6, 1, 7, 1, 7, 7, 6, 6, 9, 1, 4, 7, 3, 0, 3, 5, 9, 8, 2, 5, 3, 4, 9, 0, 4,
+        2, 8, 7, 5, 5, 4, 6, 8, 7, 3, 1, 1, 5, 9, 5, 6, 2, 8, 6, 3, 8, 8, 2, 3, 5, 3, 7, 8, 7, 5, 9, 3, 7, 5, 1, 9, 5, 7, 7, 8, 1, 8, 5, 7, 7, 8, 0, 5, 3, 2, 1, 7, 1, 2, 2, 6, 8, 0, 6, 6, 1, 3, 0, 0, 1, 9, 2, 7, 8, 7,
+        6, 6, 1, 1, 1, 9, 5, 9, 0, 9, 2, 1, 6, 4, 2, 0, 1, 9, 8, 9, 3, 8, 0, 9, 5, 2, 5, 7, 2, 0, 1, 0, 6, 5, 4, 8, 5, 8, 6, 3, 2, 7, 8, 8, 6, 5, 9, 3, 6, 1, 5, 3, 3, 8, 1, 8, 2, 7, 9, 6, 8, 2, 3, 0, 3, 0, 1, 9, 5, 2,
+        0, 3, 5, 3, 0, 1, 8, 5, 2, 9, 6, 8, 9, 9, 5, 7, 7, 3, 6, 2, 2, 5, 9, 9, 4, 1, 3, 8, 9, 1, 2, 4, 9, 7, 2, 1, 7, 7, 5, 2, 8, 3, 4, 7, 9, 1, 3, 1, 5, 1, 5, 5, 7, 4, 8, 5, 7, 2, 4, 2, 4, 5, 4, 1, 5, 0, 6, 9, 5, 9,
+        5, 0, 8, 2, 9, 5, 3, 3, 1, 1, 6, 8, 6, 1, 7, 2, 7, 8, 5, 5, 8, 8, 9, 0, 7, 5, 0, 9, 8, 3, 8, 1, 7, 5, 4, 6, 3, 7, 4, 6, 4, 9, 3, 9, 3, 1, 9, 2, 5, 5, 0, 6, 0, 4, 0, 0, 9, 2, 7, 7, 0, 1, 6, 7, 1, 1, 3, 9, 0, 0,
+        9, 8, 4, 8, 8, 2, 4, 0, 1, 2, 8, 5, 8, 3, 6, 1, 6, 0, 3, 5, 6, 3, 7, 0, 7, 6, 6, 0, 1, 0, 4, 7, 1, 0, 1, 8, 1, 9, 4, 2, 9, 5, 5, 5, 9, 6, 1, 9, 8, 9, 4, 6, 7, 6, 7, 8, 3, 7, 4, 4, 9, 4, 4, 8, 2, 5, 5, 3, 7, 9,
+        7, 7, 4, 7, 2, 6, 8, 4, 7, 1, 0, 4, 0, 4, 7, 5, 3, 4, 6, 4, 6, 2, 0, 8, 0, 4, 6, 6, 8, 4, 2, 5, 9, 0, 6, 9, 4, 9, 1, 2, 9, 3, 3, 1, 3, 6, 7, 7, 0, 2, 8, 9, 8, 9, 1, 5, 2, 1, 0, 4, 7, 5, 2, 1, 6, 2, 0, 5, 6, 9,
+        6, 6, 0, 2, 4, 0, 5, 8, 0, 3, 8, 1, 5, 0, 1, 9, 3, 5, 1, 1, 2, 5, 3, 3, 8, 2, 4, 3, 0, 0, 3, 5, 5, 8, 7, 6, 4, 0, 2, 4, 7, 4, 9, 6, 4, 7, 3, 2, 6, 3, 9, 1, 4, 1, 9, 9, 2, 7, 2, 6, 0, 4, 2, 6, 9, 9, 2, 2, 7, 9,
+        6, 7, 8, 2, 3, 5, 4, 7, 8, 1, 6, 3, 6, 0, 0, 9, 3, 4, 1, 7, 2, 1, 6, 4, 1, 2, 1, 9, 9, 2, 4, 5, 8, 6, 3, 1, 5, 0, 3, 0, 2, 8, 6, 1, 8, 2, 9, 7, 4, 5, 5, 5, 7, 0, 6, 7, 4, 9, 8, 3, 8, 5, 0, 5, 4, 9, 4, 5, 8, 8,
+        5, 8, 6, 9, 2, 6, 9, 9, 5, 6, 9, 0, 9, 2, 7, 2, 1, 0, 7, 9, 7, 5, 0, 9, 3, 0, 2, 9, 5, 5, 3, 2, 1, 1, 6, 5, 3, 4, 4, 9, 8, 7, 2, 0, 2, 7, 5, 5, 9, 6, 0, 2, 3, 6, 4, 8, 0, 6, 6, 5, 4, 9, 9, 1, 1, 9, 8, 8, 1, 8,
+        3, 4, 7, 9, 7, 7, 5, 3, 5, 6, 6, 3, 6, 9, 8, 0, 7, 4, 2, 6, 5, 4, 2, 5, 2, 7, 8, 6, 2, 5, 5, 1, 8, 1, 8, 4, 1, 7, 5, 7, 4, 6, 7, 2, 8, 9, 0, 9, 7, 7, 7, 7, 2, 7, 9, 3, 8, 0, 0, 0, 8, 1, 6, 4, 7, 0, 6, 0, 0, 1,
+        6, 1, 4, 5, 2, 4, 9, 1, 9, 2, 1, 7, 3, 2, 1, 7, 2, 1, 4, 7, 7, 2, 3, 5, 0, 1, 4, 1, 4, 4, 1, 9, 7, 3, 5, 6, 8, 5, 4, 8, 1, 6, 1, 3, 6, 1, 1, 5, 7, 3, 5, 2, 5, 5, 2, 1, 3, 3, 4, 7, 5, 7, 4, 1, 8, 4, 9, 4, 6, 8,
+        4, 3, 8, 5, 2, 3, 3, 2, 3, 9, 0, 7, 3, 9, 4, 1, 4, 3, 3, 3, 4, 5, 4, 7, 7, 6, 2, 4, 1, 6, 8, 6, 2, 5, 1, 8, 9, 8, 3, 5, 6, 9, 4, 8, 5, 5, 6, 2, 0, 9, 9, 2, 1, 9, 2, 2, 2, 1, 8, 4, 2, 7, 2, 5, 5, 0, 2, 5, 4, 2,
+        5, 6, 8, 8, 7, 6, 7, 1, 7, 9, 0, 4, 9, 4, 6, 0, 1, 6, 5, 3, 4, 6, 6, 8, 0, 4, 9, 8, 8, 6, 2, 7, 2, 3, 2, 7, 9, 1, 7, 8, 6, 0, 8, 5, 7, 8, 4, 3, 8, 3, 8, 2, 7, 9, 6, 7, 9, 7, 6, 6, 8, 1, 4, 5, 4, 1, 0, 0, 9, 5,
+        3, 8, 8, 3, 7, 8, 6, 3, 6, 0, 9, 5, 0, 6, 8, 0, 0, 6, 4, 2, 2, 5, 1, 2, 5, 2, 0, 5, 1, 1, 7, 3, 9, 2, 9, 8, 4, 8, 9, 6, 0, 8, 4, 1, 2, 8, 4, 8, 8, 6, 2, 6, 9, 4, 5, 6, 0, 4, 2, 4, 1, 9, 6, 5, 2, 8, 5, 0, 2, 2,
+        2, 1, 0, 6, 6, 1, 1, 8, 6, 3, 0, 6, 7, 4, 4, 2, 7, 8, 6, 2, 2, 0, 3, 9, 1, 9, 4, 9, 4, 5, 0, 4, 7, 1, 2, 3, 7, 1, 3, 7, 8, 6, 9, 6, 0, 9, 5, 6, 3, 6, 4, 3, 7, 1, 9, 1, 7, 2, 8, 7, 4, 6, 7, 7, 6, 4, 6, 5, 7, 5,
+        7, 3, 9, 6, 2, 4, 1, 3, 8, 9, 0, 8, 6, 5, 8, 3, 2, 6, 4, 5, 9, 9, 5, 8, 1, 3, 3, 9, 0, 4, 7, 8, 0, 2, 7, 5, 9, 0, 0, 9, 9, 4, 6, 5, 7, 6, 4, 0, 7, 8, 9, 5, 1, 2, 6, 9, 4, 6, 8, 3, 9, 8, 3, 5, 2, 5, 9, 5, 7, 0,
+        9, 8, 2, 5, 8, 2, 2, 6, 2, 0, 5, 2, 2, 4, 8, 9, 4, 0, 7, 7, 2, 6, 7, 1, 9, 4, 7, 8, 2, 6, 8, 4, 8, 2, 6, 0, 1, 4, 7, 6, 9, 9, 0, 9, 0, 2, 6, 4, 0, 1, 3, 6, 3, 9, 4, 4, 3, 7, 4, 5, 5, 3, 0, 5, 0, 6, 8, 2, 0, 3,
+        4, 9, 6, 2, 5, 2, 4, 5, 1, 7, 4, 9, 3, 9, 9, 6, 5, 1, 4, 3, 1, 4, 2, 9, 8, 0, 9, 1, 9, 0, 6, 5, 9, 2, 5, 0, 9, 3, 7, 2, 2, 1, 6, 9, 6, 4, 6, 1, 5, 1, 5, 7, 0, 9, 8, 5, 8, 3, 8, 7, 4, 1, 0, 5, 9, 7, 8, 8, 5, 9,
+        5, 9, 7, 7, 2, 9, 7, 5, 4, 9, 8, 9, 3, 0, 1, 6, 1, 7, 5, 3, 9, 2, 8, 4, 6, 8, 1, 3, 8, 2, 6, 8, 6, 8, 3, 8, 6, 8, 9, 4, 2, 7, 7, 4, 1, 5, 5, 9, 9, 1, 8, 5, 5, 9, 2, 5, 2, 4, 5, 9, 5, 3, 9, 5, 9, 4, 3, 1, 0, 4,
+        9, 9, 7, 2, 5, 2, 4, 6, 8, 0, 8, 4, 5, 9, 8, 7, 2, 7, 3, 6, 4, 4, 6, 9, 5, 8, 4, 8, 6, 5, 3, 8, 3, 6, 7, 3, 6, 2, 2, 2, 6, 2, 6, 0, 9, 9, 1, 2, 4, 6, 0, 8, 0, 5, 1, 2, 4, 3, 8, 8, 4, 3, 9, 0, 4, 5, 1, 2, 4, 4,
+        1, 3, 6, 5, 4, 9, 7, 6, 2, 7, 8, 0, 7, 9, 7, 7, 1, 5, 6, 9, 1, 4, 3, 5, 9, 9, 7, 7, 0, 0, 1, 2, 9, 6, 1, 6, 0, 8, 9, 4, 4, 1, 6, 9, 4, 8, 6, 8, 5, 5, 5, 8, 4, 8, 4, 0, 6, 3, 5, 3, 4, 2, 2, 0, 7, 2, 2, 2, 5, 8,
+        2, 8, 4, 8, 8, 6, 4, 8, 1, 5, 8, 4, 5, 6, 0, 2, 8, 5, 0, 6, 0, 1, 6, 8, 4, 2, 7, 3, 9, 4, 5, 2, 2, 6, 7, 4, 6, 7, 6, 7, 8, 8, 9, 5, 2, 5, 2, 1, 3, 8, 5, 2, 2, 5, 4, 9, 9, 5, 4, 6, 6, 6, 7, 2, 7, 8, 2, 3, 9, 8,
+        6, 4, 5, 6, 5, 9, 6, 1, 1, 6, 3, 5, 4, 8, 8, 6, 2, 3, 0, 5, 7, 7, 4, 5, 6, 4, 9, 8, 0, 3, 5, 5, 9, 3, 6, 3, 4, 5, 6, 8, 1, 7, 4, 3, 2, 4, 1, 1, 2, 5, 1, 5, 0, 7, 6, 0, 6, 9, 4, 7, 9, 4, 5, 1, 0, 9, 6, 5, 9, 6,
+        0, 9, 4, 0, 2, 5, 2, 2, 8, 8, 7, 9, 7, 1, 0, 8, 9, 3, 1, 4, 5, 6, 6, 9, 1, 3, 6, 8, 6, 7, 2, 2, 8, 7, 4, 8, 9, 4, 0, 5, 6, 0, 1, 0, 1, 5, 0, 3, 3, 0, 8, 6, 1, 7, 9, 2, 8, 6, 8, 0, 9, 2, 0, 8, 7, 4, 7, 6, 0, 9,
+        1, 7, 8, 2, 4, 9, 3, 8, 5, 8, 9, 0, 0, 9, 7, 1, 4, 9, 0, 9, 6, 7, 5, 9, 8, 5, 2, 6, 1, 3, 6, 5, 5, 4, 9, 7, 8, 1, 8, 9, 3, 1, 2, 9, 7, 8, 4, 8, 2, 1, 6, 8, 2, 9, 9, 8, 9, 4, 8, 7, 2, 2, 6, 5, 8, 8, 0, 4, 8, 5,
+        7, 5, 6, 4, 0, 1, 4, 2, 7, 0, 4, 7, 7, 5, 5, 5, 1, 3, 2, 3, 7, 9, 6, 4, 1, 4, 5, 1, 5, 2, 3, 7, 4, 6, 2, 3, 4, 3, 6, 4, 5, 4, 2, 8, 5, 8, 4, 4, 4, 7, 9, 5, 2, 6, 5, 8, 6, 7, 8, 2, 1, 0, 5, 1, 1, 4, 1, 3, 5, 4,
+        7, 3, 5, 7, 3, 9, 5, 2, 3, 1, 1, 3, 4, 2, 7, 1, 6, 6, 1, 0, 2, 1, 3, 5, 9, 6, 9, 5, 3, 6, 2, 3, 1, 4, 4, 2, 9, 5, 2, 4, 8, 4, 9, 3, 7, 1, 8, 7, 1, 1, 0, 1, 4, 5, 7, 6, 5, 4, 0, 3, 5, 9, 0, 2, 7, 9, 9, 3, 4, 4,
+        0, 3, 7, 4, 2, 0, 0, 7, 3, 1, 0, 5, 7, 8, 5, 3, 9, 0, 6, 2, 1, 9, 8, 3, 8, 7, 4, 4, 7, 8, 0, 8, 4, 7, 8, 4, 8, 9, 6, 8, 3, 3, 2, 1, 4, 4, 5, 7, 1, 3, 8, 6, 8, 7, 5, 1, 9, 4, 3, 5, 0, 6, 4, 3, 0, 2, 1, 8, 4, 5,
+        3, 1, 9, 1, 0, 4, 8, 4, 8, 1, 0, 0, 5, 3, 7, 0, 6, 1, 4, 6, 8, 0, 6, 7, 4, 9, 1, 9, 2, 7, 8, 1, 9, 1, 1, 9, 7, 9, 3, 9, 9, 5, 2, 0, 6, 1, 4, 1, 9, 6, 6, 3, 4, 2, 8, 7, 5, 4, 4, 4, 0, 6, 4, 3, 7, 4, 5, 1, 2, 3,
+        7, 1, 8, 1, 9, 2, 1, 7, 9, 9, 9, 8, 3, 9, 1, 0, 1, 5, 9, 1, 9, 5, 6, 1, 8, 1, 4, 6, 7, 5, 1, 4, 2, 6, 9, 1, 2, 3, 9, 7, 4, 8, 9, 4, 0, 9, 0, 7, 1, 8, 6, 4, 9, 4, 2, 3, 1, 9, 6, 1, 5, 6, 7, 9, 4, 5, 2, 0, 8, 0,
+        9, 5, 1, 4, 6, 5, 5, 0, 2, 2, 5, 2, 3, 1, 6, 0, 3, 8, 8, 1, 9, 3, 0, 1, 4, 2, 0, 9, 3, 7, 6, 2, 1, 3, 7, 8, 5, 5, 9, 5, 6, 6, 3, 8, 9, 3, 7, 7, 8, 7, 0, 8, 3, 0, 3, 9, 0, 6, 9, 7, 9, 2, 0, 7, 7, 3, 4, 6, 7, 2,
+        2, 1, 8, 2, 5, 6, 2, 5, 9, 9, 6, 6, 1, 5, 0, 1, 4, 2, 1, 5, 0, 3, 0, 6, 8, 0, 3, 8, 4, 4, 7, 7, 3, 4, 5, 4, 9, 2, 0, 2, 6, 0, 5, 4, 1, 4, 6, 6, 5, 9, 2, 5, 2, 0, 1, 4, 9, 7, 4, 4, 2, 8, 5, 0, 7, 3, 2, 5, 1, 8,
+        6, 6, 6, 0, 0, 2, 1, 3, 2, 4, 3, 4, 0, 8, 8, 1, 9, 0, 7, 1, 0, 4, 8, 6, 3, 3, 1, 7, 3, 4, 6, 4, 9, 6, 5, 1, 4, 5, 3, 9, 0, 5, 7, 9, 6, 2, 6, 8, 5, 6, 1, 0, 0, 5, 5, 0, 8, 1, 0, 6, 6, 5, 8, 7, 9, 6, 9, 9, 8, 1,
+        6, 3, 5, 7, 4, 7, 3, 6, 3, 8, 4, 0, 5, 2, 5, 7, 1, 4, 5, 9, 1, 0, 2, 8, 9, 7, 0, 6, 4, 1, 4, 0, 1, 1, 0, 9, 7, 1, 2, 0, 6, 2, 8, 0, 4, 3, 9, 0, 3, 9, 7, 5, 9, 5, 1, 5, 6, 7, 7, 1, 5, 7, 7, 0, 0, 4, 2, 0, 3, 3,
+        7, 8, 6, 9, 9, 3, 6, 0, 0, 7, 2, 3, 0, 5, 5, 8, 7, 6, 3, 1, 7, 6, 3, 5, 9, 4, 2, 1, 8, 7, 3, 1, 2, 5, 1, 4, 7, 1, 2, 0, 5, 3, 2, 9, 2, 8, 1, 9, 1, 8, 2, 6, 1, 8, 6, 1, 2, 5, 8, 6, 7, 3, 2, 1, 5, 7, 9, 1, 9, 8,
+        4, 1, 4, 8, 4, 8, 8, 2, 9, 1, 6, 4, 4, 7, 0, 6, 0, 9, 5, 7, 5, 2, 7, 0, 6, 9, 5, 7, 2, 2, 0, 9, 1, 7, 5, 6, 7, 1, 1, 6, 7, 2, 2, 9, 1, 0, 9, 8, 1, 6, 9, 0, 9, 1, 5, 2, 8, 0, 1, 7, 3, 5, 0, 6, 7, 1, 2, 7, 4, 8,
+        5, 8, 3, 2, 2, 2, 8, 7, 1, 8, 3, 5, 2, 0, 9, 3, 5, 3, 9, 6, 5, 7, 2, 5, 1, 2, 1, 0, 8, 3, 5, 7, 9, 1, 5, 1, 3, 6, 9, 8, 8, 2, 0, 9, 1, 4, 4, 4, 2, 1, 0, 0, 6, 7, 5, 1, 0, 3, 3, 4, 6, 7, 1, 1, 0, 3, 1, 4, 1, 2,
+        6, 7, 1, 1, 1, 3, 6, 9, 9, 0, 8, 6, 5, 8, 5, 1, 6, 3, 9, 8, 3, 1, 5, 0, 1, 9, 7, 0, 1, 6, 5, 1, 5, 1, 1, 6, 8, 5, 1, 7, 1, 4, 3, 7, 6, 5, 7, 6, 1, 8, 3, 5, 1, 5, 5, 6, 5, 0, 8, 8, 4, 9, 0, 9, 9, 8, 9, 8, 5, 9,
+        9, 8, 2, 3, 8, 7, 3, 4, 5, 5, 2, 8, 3, 3, 1, 6, 3, 5, 5, 0, 7, 6, 4, 7, 9, 1, 8, 5, 3, 5, 8, 9, 3, 2, 2, 6, 1, 8, 5, 4, 8, 9, 6, 3, 2, 1, 3, 2, 9, 3, 3, 0, 8, 9, 8, 5, 7, 0, 6, 4, 2, 0, 4, 6, 7, 5, 2, 5, 9, 0,
+        7, 0, 9, 1, 5, 4, 8, 1, 4, 1, 6, 5, 4, 9, 8, 5, 9, 4, 6, 1, 6, 3, 7, 1, 8, 0, 2, 7, 0, 9, 8, 1, 9, 9, 4, 3, 0, 9, 9, 2, 4, 4, 8, 8, 9, 5, 7, 5, 7, 1, 2, 8, 2, 8, 9, 0, 5, 9, 2, 3, 2, 3, 3, 2, 6, 0, 9, 7, 2, 9,
+        9, 7, 1, 2, 0, 8, 4, 4, 3, 3, 5, 7, 3, 2, 6, 5, 4, 8, 9, 3, 8, 2, 3, 9, 1, 1, 9, 3, 2, 5, 9, 7, 4, 6, 3, 6, 6, 7, 3, 0, 5, 8, 3, 6, 0, 4, 1, 4, 2, 8, 1, 3, 8, 8, 3, 0, 3, 2, 0, 3, 8, 2, 4, 9, 0, 3, 7, 5, 8, 9,
+        8, 5, 2, 4, 3, 7, 4, 4, 1, 7, 0, 2, 9, 1, 3, 2, 7, 6, 5, 6, 1, 8, 0, 9, 3, 7, 7, 3, 4, 4, 4, 0, 3, 0, 7, 0, 7, 4, 6, 9, 2, 1, 1, 2, 0, 1, 9, 1, 3, 0, 2, 0, 3, 3, 0, 3, 8, 0, 1, 9, 7, 6, 2, 1, 1, 0, 1, 1, 0, 0,
+        4, 4, 9, 2, 9, 3, 2, 1, 5, 1, 6, 0, 8, 4, 2, 4, 4, 4, 8, 5, 9, 6, 3, 7, 6, 6, 9, 8, 3, 8, 9, 5, 2, 2, 8, 6, 8, 4, 7, 8, 3, 1, 2, 3, 5, 5, 2, 6, 5, 8, 2, 1, 3, 1, 4, 4, 9, 5, 7, 6, 8, 5, 7, 2, 6, 2, 4, 3, 3, 4,
+        4, 1, 8, 9, 3, 0, 3, 9, 6, 8, 6, 4, 2, 6, 2, 4, 3, 4, 1, 0, 7, 7, 3, 2, 2, 6, 9, 7, 8, 0, 2, 8, 0, 7, 3, 1, 8, 9, 1, 5, 4, 4, 1, 1, 0, 1, 0, 4, 4, 6, 8, 2, 3, 2, 5, 2, 7, 1, 6, 2, 0, 1, 0, 5, 2, 6, 5, 2, 2, 7,
+        2, 1, 1, 1, 6, 6, 0, 3, 9, 6, 6, 6, 5, 5, 7, 3, 0, 9, 2, 5, 4, 7, 1, 1, 0, 5, 5, 7, 8, 5, 3, 7, 6, 3, 4, 6, 6, 8, 2, 0, 6, 5, 3, 1, 0, 9, 8, 9, 6, 5, 2, 6, 9, 1, 8, 6, 2, 0, 5, 6, 4, 7, 6, 9, 3, 1, 2, 5, 7, 0,
+        5, 8, 6, 3, 5, 6, 6, 2, 0, 1, 8, 5, 5, 8, 1, 0, 0, 7, 2, 9, 3, 6, 0, 6, 5, 9, 8, 7, 6, 4, 8, 6, 1, 1, 7, 9, 1, 0, 4, 5, 3, 3, 4, 8, 8, 5, 0, 3, 4, 6, 1, 1, 3, 6, 5, 7, 6, 8, 6, 7, 5, 3, 2, 4, 9, 4, 4, 1, 6, 6,
+        8, 0, 3, 9, 6, 2, 6, 5, 7, 9, 7, 8, 7, 7, 1, 8, 5, 5, 6, 0, 8, 4, 5, 5, 2, 9, 6, 5, 4, 1, 2, 6, 6, 5, 4, 0, 8, 5, 3, 0, 6, 1, 4, 3, 4, 4, 4, 3, 1, 8, 5, 8, 6, 7, 6, 9, 7, 5, 1, 4, 5, 6, 6, 1, 4, 0, 6, 8, 0, 0,
+        7, 0, 0, 2, 3, 7, 8, 7, 7, 6, 5, 9, 1, 3, 4, 4, 0, 1, 7, 1, 2, 7, 4, 9, 4, 7, 0, 4, 2, 0, 5, 6, 2, 2, 3, 0, 5, 3, 8, 9, 9, 4, 5, 6, 1, 3, 1, 4, 0, 7, 1, 1, 2, 7, 0, 0, 0, 4, 0, 7, 8, 5, 4, 7, 3, 3, 2, 6, 9, 9,
+        3, 9, 0, 8, 1, 4, 5, 4, 6, 6, 4, 6, 4, 5, 8, 8, 0, 7, 9, 7, 2, 7, 0, 8, 2, 6, 6, 8, 3, 0, 6, 3, 4, 3, 2, 8, 5, 8, 7, 8, 5, 6, 9, 8, 3, 0, 5, 2, 3, 5, 8, 0, 8, 9, 3, 3, 0, 6, 5, 7, 5, 7, 4, 0, 6, 7, 9, 5, 4, 5,
+        7, 1, 6, 3, 7, 7, 5, 2, 5, 4, 2, 0, 2, 1, 1, 4, 9, 5, 5, 7, 6, 1, 5, 8, 1, 4, 0, 0, 2, 5, 0, 1, 2, 6, 2, 2, 8, 5, 9, 4, 1, 3, 0, 2, 1, 6, 4, 7, 1, 5, 5, 0, 9, 7, 9, 2, 5, 9, 2, 3, 0, 9, 9, 0, 7, 9, 6, 5, 4, 7,
+        3, 7, 6, 1, 2, 5, 5, 1, 7, 6, 5, 6, 7, 5, 1, 3, 5, 7, 5, 1, 7, 8, 2, 9, 6, 6, 6, 4, 5, 4, 7, 7, 9, 1, 7, 4, 5, 0, 1, 1, 2, 9, 9, 6, 1, 4, 8, 9, 0, 3, 0, 4, 6, 3, 9, 9, 4, 7, 1, 3, 2, 9, 6, 2, 1, 0, 7, 3, 4, 0,
+        4, 3, 7, 5, 1, 8, 9, 5, 7, 3, 5, 9, 6, 1, 4, 5, 8, 9, 0, 1, 9, 3, 8, 9, 7, 1, 3, 1, 1, 1, 7, 9, 0, 4, 2, 9, 7, 8, 2, 8, 5, 6, 4, 7, 5, 0, 3, 2, 0, 3, 1, 9, 8, 6, 9, 1, 5, 1, 4, 0, 2, 8, 7, 0, 8, 0, 8, 5, 9, 9,
+        0, 4, 8, 0, 1, 0, 9, 4, 1, 2, 1, 4, 7, 2, 2, 1, 3, 1, 7, 9, 4, 7, 6, 4, 7, 7, 7, 2, 6, 2, 2, 4, 1, 4, 2, 5, 4, 8, 5, 4, 5, 4, 0, 3, 3, 2, 1, 5, 7, 1, 8, 5, 3, 0, 6, 1, 4, 2, 2, 8, 8, 1, 3, 7, 5, 8, 5, 0, 4, 3,
+        0, 6, 3, 3, 2, 1, 7, 5, 1, 8, 2, 9, 7, 9, 8, 6, 6, 2, 2, 3, 7, 1, 7, 2, 1, 5, 9, 1, 6, 0, 7, 7, 1, 6, 6, 9, 2, 5, 4, 7, 4, 8, 7, 3, 8, 9, 8, 6, 6, 5, 4, 9, 4, 9, 4, 5, 0, 1, 1, 4, 6, 5, 4, 0, 6, 2, 8, 4, 3, 3,
+        6, 6, 3, 9, 3, 7, 9, 0, 0, 3, 9, 7, 6, 9, 2, 6, 5, 6, 7, 2, 1, 4, 6, 3, 8, 5, 3, 0, 6, 7, 3, 6, 0, 9, 6, 5, 7, 1, 2, 0, 9, 1, 8, 0, 7, 6, 3, 8, 3, 2, 7, 1, 6, 6, 4, 1, 6, 2, 7, 4, 8, 8, 8, 8, 0, 0, 7, 8, 6, 9,
+        2, 5, 6, 0, 2, 9, 0, 2, 2, 8, 4, 7, 2, 1, 0, 4, 0, 3, 1, 7, 2, 1, 1, 8, 6, 0, 8, 2, 0, 4, 1, 9, 0, 0, 0, 4, 2, 2, 9, 6, 6, 1, 7, 1, 1, 9, 6, 3, 7, 7, 9, 2, 1, 3, 3, 7, 5, 7, 5, 1, 1, 4, 9, 5, 9, 5, 0, 1, 5, 6,
+        6, 0, 4, 9, 6, 3, 1, 8, 6, 2, 9, 4, 7, 2, 6, 5, 4, 7, 3, 6, 4, 2, 5, 2, 3, 0, 8, 1, 7, 7, 0, 3, 6, 7, 5, 1, 5, 9, 0, 6, 7, 3, 5, 0, 2, 3, 5, 0, 7, 2, 8, 3, 5, 4, 0, 5, 6, 7, 0, 4, 0, 3, 8, 6, 7, 4, 3, 5, 1, 3,
+        6, 2, 2, 2, 2, 4, 7, 7, 1, 5, 8, 9, 1, 5, 0, 4, 9, 5, 3, 0, 9, 8, 4, 4, 4, 8, 9, 3, 3, 3, 0, 9, 6, 3, 4, 0, 8, 7, 8, 0, 7, 6, 9, 3, 2, 5, 9, 9, 3, 9, 7, 8, 0, 5, 4, 1, 9, 3, 4, 1, 4, 4, 7, 3, 7, 7, 4, 4, 1, 8,
+        4, 2, 6, 3, 1, 2, 9, 8, 6, 0, 8, 0, 9, 9, 8, 8, 8, 6, 8, 7, 4, 1, 3, 2, 6, 0, 4, 7, 2, 1, 5, 6, 9, 5, 1, 6, 2, 3, 9, 6, 5, 8, 6, 4, 5, 7, 3, 0, 2, 1, 6, 3, 1, 5, 9, 8, 1, 9, 3, 1, 9, 5, 1, 6, 7, 3, 5, 3, 8, 1,
+        2, 9, 7, 4, 1, 6, 7, 7, 2, 9, 4, 7, 8, 6, 7, 2, 4, 2, 2, 9, 2, 4, 6, 5, 4, 3, 6, 6, 8, 0, 0, 9, 8, 0, 6, 7, 6, 9, 2, 8, 2, 3, 8, 2, 8, 0, 6, 8, 9, 9, 6, 4, 0, 0, 4, 8, 2, 4, 3, 5, 4, 0, 3, 7, 0, 1, 4, 1, 6, 3,
+        1, 4, 9, 6, 5, 8, 9, 7, 9, 4, 0, 9, 2, 4, 3, 2, 3, 7, 8, 9, 6, 9, 0, 7, 0, 6, 9, 7, 7, 9, 4, 2, 2, 3, 6, 2, 5, 0, 8, 2, 2, 1, 6, 8, 8, 9, 5, 7, 3, 8, 3, 7, 9, 8, 6, 2, 3, 0, 0, 1, 5, 9, 3, 7, 7, 6, 4, 7, 1, 6,
+        5, 1, 2, 2, 8, 9, 3, 5, 7, 8, 6, 0, 1, 5, 8, 8, 1, 6, 1, 7, 5, 5, 7, 8, 2, 9, 7, 3, 5, 2, 3, 3, 4, 4, 6, 0, 4, 2, 8, 1, 5, 1, 2, 6, 2, 7, 2, 0, 3, 7, 3, 4, 3, 1, 4, 6, 5, 3, 1, 9, 7, 7, 7, 7, 4, 1, 6, 0, 3, 1,
+        9, 9, 0, 6, 6, 5, 5, 4, 1, 8, 7, 6, 3, 9, 7, 9, 2, 9, 3, 3, 4, 4, 1, 9, 5, 2, 1, 5, 4, 1, 3, 4, 1, 8, 9, 9, 4, 8, 5, 4, 4, 4, 7, 3, 4, 5, 6, 7, 3, 8, 3, 1, 6, 2, 4, 9, 9, 3, 4, 1, 9, 1, 3, 1, 8, 1, 4, 8, 0, 9,
+        2, 7, 7, 7, 7, 1, 0, 3, 8, 6, 3, 8, 7, 7, 3, 4, 3, 1, 7, 7, 2, 0, 7, 5, 4, 5, 6, 5, 4, 5, 3, 2, 2, 0, 7, 7, 7, 0, 9, 2, 1, 2, 0, 1, 9, 0, 5, 1, 6, 6, 0, 9, 6, 2, 8, 0, 4, 9, 0, 9, 2, 6, 3, 6, 0, 1, 9, 7, 5, 9,
+        8, 8, 2, 8, 1, 6, 1, 3, 3, 2, 3, 1, 6, 6, 6, 3, 6, 5, 2, 8, 6, 1, 9, 3, 2, 6, 6, 8, 6, 3, 3, 6, 0, 6, 2, 7, 3, 5, 6, 7, 6, 3, 0, 3, 5, 4, 4, 7, 7, 6, 2, 8, 0, 3, 5, 0, 4, 5, 0, 7, 7, 7, 2, 3, 5, 5, 4, 7, 1, 0,
+        5, 8, 5, 9, 5, 4, 8, 7, 0, 2, 7, 9, 0, 8, 1, 4, 3, 5, 6, 2, 4, 0, 1, 4, 5, 1, 7, 1, 8, 0, 6, 2, 4, 6, 4, 3, 6, 2, 6, 7, 9, 4, 5, 6, 1, 2, 7, 5, 3, 1, 8, 1, 3, 4, 0, 7, 8, 3, 3, 0, 3, 3, 6, 2, 5, 4, 2, 3, 2, 7,
+        8, 3, 9, 4, 4, 9, 7, 5, 3, 8, 2, 4, 3, 7, 2, 0, 5, 8, 3, 5, 3, 1, 1, 4, 7, 7, 1, 1, 9, 9, 2, 6, 0, 6, 3, 8, 1, 3, 3, 4, 6, 7, 7, 6, 8, 7, 9, 6, 9, 5, 9, 7, 0, 3, 0, 9, 8, 3, 3, 9, 1, 3, 0, 7, 7, 1, 0, 9, 8, 7,
+        0, 4, 0, 8, 5, 9, 1, 3, 3, 7, 4, 6, 4, 1, 4, 4, 2, 8, 2, 2, 7, 7, 2, 6, 3, 4, 6, 5, 9, 4, 7, 0, 4, 7, 4, 5, 8, 7, 8, 4, 7, 7, 8, 7, 2, 0, 1, 9, 2, 7, 7, 1, 5, 2, 8, 0, 7, 3, 1, 7, 6, 7, 9, 0, 7, 7, 0, 7, 1, 5,
+        7, 2, 1, 3, 4, 4, 4, 7, 3, 0, 6, 0, 5, 7, 0, 0, 7, 3, 3, 4, 9, 2, 4, 3, 6, 9, 3, 1, 1, 3, 8, 3, 5, 0, 4, 9, 3, 1, 6, 3, 1, 2, 8, 4, 0, 4, 2, 5, 1, 2, 1, 9, 2, 5, 6, 5, 1, 7, 9, 8, 0, 6, 9, 4, 1, 1, 3, 5, 2, 8,
+        0, 1, 3, 1, 4, 7, 0, 1, 3, 0, 4, 7, 8, 1, 6, 4, 3, 7, 8, 8, 5, 1, 8, 5, 2, 9, 0, 9, 2, 8, 5, 4, 5, 2, 0, 1, 1, 6, 5, 8, 3, 9, 3, 4, 1, 9, 6, 5, 6, 2, 1, 3, 4, 9, 1, 4, 3, 4, 1, 5, 9, 5, 6, 2, 5, 8, 6, 5, 8, 6,
+        5, 5, 7, 0, 5, 5, 2, 6, 9, 0, 4, 9, 6, 5, 2, 0, 9, 8, 5, 8, 0, 3, 3, 8, 5, 0, 7, 2, 2, 4, 2, 6, 4, 8, 2, 9, 3, 9, 7, 2, 8, 5, 8, 4, 7, 8, 3, 1, 6, 3, 0, 5, 7, 7, 7, 7, 5, 6, 0, 6, 8, 8, 8, 7, 6, 4, 4, 6, 2, 4,
+        8, 2, 4, 6, 8, 5, 7, 9, 2, 6, 0, 3, 9, 5, 3, 5, 2, 7, 7, 3, 4, 8, 0, 3, 0, 4, 8, 0, 2, 9, 0, 0, 5, 8, 7, 6, 0, 7, 5, 8, 2, 5, 1, 0, 4, 7, 4, 7, 0, 9, 1, 6, 4, 3, 9, 6, 1, 3, 6, 2, 6, 7, 6, 0, 4, 4, 9, 2, 5, 6,
+        2, 7, 4, 2, 0, 4, 2, 0, 8, 3, 2, 0, 8, 5, 6, 6, 1, 1, 9, 0, 6, 2, 5, 4, 5, 4, 3, 3, 7, 2, 1, 3, 1, 5, 3, 5, 9, 5, 8, 4, 5, 0, 6, 8, 7, 7, 2, 4, 6, 0, 2, 9, 0, 1, 6, 1, 8, 7, 6, 6, 7, 9, 5, 2, 4, 0, 6, 1, 6, 3,
+        4, 2, 5, 2, 2, 5, 7, 7, 1, 9, 5, 4, 2, 9, 1, 6, 2, 9, 9, 1, 9, 3, 0, 6, 4, 5, 5, 3, 7, 7, 9, 9, 1, 4, 0, 3, 7, 3, 4, 0, 4, 3, 2, 8, 7, 5, 2, 6, 2, 8, 8, 8, 9, 6, 3, 9, 9, 5, 8, 7, 9, 4, 7, 5, 7, 2, 9, 1, 7, 4,
+        6, 4, 2, 6, 3, 5, 7, 4, 5, 5, 2, 5, 4, 0, 7, 9, 0, 9, 1, 4, 5, 1, 3, 5, 7, 1, 1, 1, 3, 6, 9, 4, 1, 0, 9, 1, 1, 9, 3, 9, 3, 2, 5, 1, 9, 1, 0, 7, 6, 0, 2, 0, 8, 2, 5, 2, 0, 2, 6, 1, 8, 7, 9, 8, 5, 3, 1, 8, 8, 7,
+        7, 0, 5, 8, 4, 2, 9, 7, 2, 5, 9, 1, 6, 7, 7, 8, 1, 3, 1, 4, 9, 6, 9, 9, 0, 0, 9, 0, 1, 9, 2, 1, 1, 6, 9, 7, 1, 7, 3, 7, 2, 7, 8, 4, 7, 6, 8, 4, 7, 2, 6, 8, 6, 0, 8, 4, 9, 0, 0, 3, 3, 7, 7, 0, 2, 4, 2, 4, 2, 9,
+        1, 6, 5, 1, 3, 0, 0, 5, 0, 0, 5, 1, 6, 8, 3, 2, 3, 3, 6, 4, 3, 5, 0, 3, 8, 9, 5, 1, 7, 0, 2, 9, 8, 9, 3, 9, 2, 2, 3, 3, 4, 5, 1, 7, 2, 2, 0, 1, 3, 8, 1, 2, 8, 0, 6, 9, 6, 5, 0, 1, 1, 7, 8, 4, 4, 0, 8, 7, 4, 5,
+        1, 9, 6, 0, 1, 2, 1, 2, 2, 8, 5, 9, 9, 3, 7, 1, 6, 2, 3, 1, 3, 0, 1, 7, 1, 1, 4, 4, 4, 8, 4, 6, 4, 0, 9, 0, 3, 8, 9, 0, 6, 4, 4, 9, 5, 4, 4, 4, 0, 0, 6, 1, 9, 8, 6, 9, 0, 7, 5, 4, 8, 5, 1, 6, 0, 2, 6, 3, 2, 7,
+        5, 0, 5, 2, 9, 8, 3, 4, 9, 1, 8, 7, 4, 0, 7, 8, 6, 6, 8, 0, 8, 8, 1, 8, 3, 3, 8, 5, 1, 0, 2, 2, 8, 3, 3, 4, 5, 0, 8, 5, 0, 4, 8, 6, 0, 8, 2, 5, 0, 3, 9, 3, 0, 2, 1, 3, 3, 2, 1, 9, 7, 1, 5, 5, 1, 8, 4, 3, 0, 6,
+        3, 5, 4, 5, 5, 0, 0, 7, 6, 6, 8, 2, 8, 2, 9, 4, 9, 3, 0, 4, 1, 3, 7, 7, 6, 5, 5, 2, 7, 9, 3, 9, 7, 5, 1, 7, 5, 4, 6, 1, 3, 9, 5, 3, 9, 8, 4, 6, 8, 3, 3, 9, 3, 6, 3, 8, 3, 0, 4, 7, 4, 6, 1, 1, 9, 9, 6, 6, 5, 3,
+        8, 5, 8, 1, 5, 3, 8, 4, 2, 0, 5, 6, 8, 5, 3, 3, 8, 6, 2, 1, 8, 6, 7, 2, 5, 2, 3, 3, 4, 0, 2, 8, 3, 0, 8, 7, 1, 1, 2, 3, 2, 8, 2, 7, 8, 9, 2, 1, 2, 5, 0, 7, 7, 1, 2, 6, 2, 9, 4, 6, 3, 2, 2, 9, 5, 6, 3, 9, 8, 9,
+        8, 9, 8, 9, 3, 5, 8, 2, 1, 1, 6, 7, 4, 5, 6, 2, 7, 0, 1, 0, 2, 1, 8, 3, 5, 6, 4, 6, 2, 2, 0, 1, 3, 4, 9, 6, 7, 1, 5, 1, 8, 8, 1, 9, 0, 9, 7, 3, 0, 3, 8, 1, 1, 9, 8, 0, 0, 4, 9, 7, 3, 4, 0, 7, 2, 3, 9, 6, 1, 0,
+        3, 6, 8, 5, 4, 0, 6, 6, 4, 3, 1, 9, 3, 9, 5, 0, 9, 7, 9, 0, 1, 9, 0, 6, 9, 9, 6, 3, 9, 5, 5, 2, 4, 5, 3, 0, 0, 5, 4, 5, 0, 5, 8, 0, 6, 8, 5, 5, 0, 1, 9, 5, 6, 7, 3, 0, 2, 2, 9, 2, 1, 9, 1, 3, 9, 3, 3, 9, 1, 8,
+        5, 6, 8, 0, 3, 4, 4, 9, 0, 3, 9, 8, 2, 0, 5, 9, 5, 5, 1, 0, 0, 2, 2, 6, 3, 5, 3, 5, 3, 6, 1, 9, 2, 0, 4, 1, 9, 9, 4, 7, 4, 5, 5, 3, 8, 5, 9, 3, 8, 1, 0, 2, 3, 4, 3, 9, 5, 5, 4, 4, 9, 5, 9, 7, 7, 8, 3, 7, 7, 9,
+        0, 2, 3, 7, 4, 2, 1, 6, 1, 7, 2, 7, 1, 1, 1, 7, 2, 3, 6, 4, 3, 4, 3, 5, 4, 3, 9, 4, 7, 8, 2, 2, 1, 8, 1, 8, 5, 2, 8, 6, 2, 4, 0, 8, 5, 1, 4, 0, 0, 6, 6, 6, 0, 4, 4, 3, 3, 2, 5, 8, 8, 8, 5, 6, 9, 8, 6, 7, 0, 5,
+        4, 3, 1, 5, 4, 7, 0, 6, 9, 6, 5, 7, 4, 7, 4, 5, 8, 5, 5, 0, 3, 3, 2, 3, 2, 3, 3, 4, 2, 1, 0, 7, 3, 0, 1, 5, 4, 5, 9, 4, 0, 5, 1, 6, 5, 5, 3, 7, 9, 0, 6, 8, 6, 6, 2, 7, 3, 3, 3, 7, 9, 9, 5, 8, 5, 1, 1, 5, 6, 2,
+        5, 7, 8, 4, 3, 2, 2, 9, 8, 8, 2, 7, 3, 7, 2, 3, 1, 9, 8, 9, 8, 7, 5, 7, 1, 4, 1, 5, 9, 5, 7, 8, 1, 1, 1, 9, 6, 3, 5, 8, 3, 3, 0, 0, 5, 9, 4, 0, 8, 7, 3, 0, 6, 8, 1, 2, 1, 6, 0, 2, 8, 7, 6, 4, 9, 6, 2, 8, 6, 7,
+        4, 4, 6, 0, 4, 7, 7, 4, 6, 4, 9, 1, 5, 9, 9, 5, 0, 5, 4, 9, 7, 3, 7, 4, 2, 5, 6, 2, 6, 9, 0, 1, 0, 4, 9, 0, 3, 7, 7, 8, 1, 9, 8, 6, 8, 3, 5, 9, 3, 8, 1, 4, 6, 5, 7, 4, 1, 2, 6, 8, 0, 4, 9, 2, 5, 6, 4, 8, 7, 9,
+        8, 5, 5, 6, 1, 4, 5, 3, 7, 2, 3, 4, 7, 8, 6, 7, 3, 3, 0, 3, 9, 0, 4, 6, 8, 8, 3, 8, 3, 4, 3, 6, 3, 4, 6, 5, 5, 3, 7, 9, 4, 9, 8, 6, 4, 1, 9, 2, 7, 0, 5, 6, 3, 8, 7, 2, 9, 3, 1, 7, 4, 8, 7, 2, 3, 3, 2, 0, 8, 3,
+        7, 6, 0, 1, 1, 2, 3, 0, 2, 9, 9, 1, 1, 3, 6, 7, 9, 3, 8, 6, 2, 7, 0, 8, 9, 4, 3, 8, 7, 9, 9, 3, 6, 2, 0, 1, 6, 2, 9, 5, 1, 5, 4, 1, 3, 3, 7, 1, 4, 2, 4, 8, 9, 2, 8, 3, 0, 7, 2, 2, 0, 1, 2, 6, 9, 0, 1, 4, 7, 5,
+        4, 6, 6, 8, 4, 7, 6, 5, 3, 5, 7, 6, 1, 6, 4, 7, 7, 3, 7, 9, 4, 6, 7, 5, 2, 0, 0, 4, 9, 0, 7, 5, 7, 1, 5, 5, 5, 2, 7, 8, 1, 9, 6, 5, 3, 6, 2, 1, 3, 2, 3, 9, 2, 6, 4, 0, 6, 1, 6, 0, 1, 3, 6, 3, 5, 8, 1, 5, 5, 9,
+        0, 7, 4, 2, 2, 0, 2, 0, 2, 0, 3, 1, 8, 7, 2, 7, 7, 6, 0, 5, 2, 7, 7, 2, 1, 9, 0, 0, 5, 5, 6, 1, 4, 8, 4, 2, 5, 5, 5, 1, 8, 7, 9, 2, 5, 3, 0, 3, 4, 3, 5, 1, 3, 9, 8, 4, 4, 2, 5, 3, 2, 2, 3, 4, 1, 5, 7, 6, 2, 3,
+        3, 6, 1, 0, 6, 4, 2, 5, 0, 6, 3, 9, 0, 4, 9, 7, 5, 0, 0, 8, 6, 5, 6, 2, 7, 1, 0, 9, 5, 3, 5, 9, 1, 9, 4, 6, 5, 8, 9, 7, 5, 1, 4, 1, 3, 1, 0, 3, 4, 8, 2, 2, 7, 6, 9, 3, 0, 6, 2, 4, 7, 4, 3, 5, 3, 6, 3, 2, 5, 6,
+        9, 1, 6, 0, 7, 8, 1, 5, 4, 7, 8, 1, 8, 1, 1, 5, 2, 8, 4, 3, 6, 6, 7, 9, 5, 7, 0, 6, 1, 1, 0, 8, 6, 1, 5, 3, 3, 1, 5, 0, 4, 4, 5, 2, 1, 2, 7, 4, 7, 3, 9, 2, 4, 5, 4, 4, 9, 4, 5, 4, 2, 3, 6, 8, 2, 8, 8, 6, 0, 6,
+        1, 3, 4, 0, 8, 4, 1, 4, 8, 6, 3, 7, 7, 6, 7, 0, 0, 9, 6, 1, 2, 0, 7, 1, 5, 1, 2, 4, 9, 1, 4, 0, 4, 3, 0, 2, 7, 2, 5, 3, 8, 6, 0, 7, 6, 4, 8, 2, 3, 6, 3, 4, 1, 4, 3, 3, 4, 6, 2, 3, 5, 1, 8, 9, 7, 5, 7, 6, 6, 4,
+        5, 2, 1, 6, 4, 1, 3, 7, 6, 7, 9, 6, 9, 0, 3, 1, 4, 9, 5, 0, 1, 9, 1, 0, 8, 5, 7, 5, 9, 8, 4, 4, 2, 3, 9, 1, 9, 8, 6, 2, 9, 1, 6, 4, 2, 1, 9, 3, 9, 9, 4, 9, 0, 7, 2, 3, 6, 2, 3, 4, 6, 4, 6, 8, 4, 4, 1, 1, 7, 3,
+        9, 4, 0, 3, 2, 6, 5, 9, 1, 8, 4, 0, 4, 4, 3, 7, 8, 0, 5, 1, 3, 3, 3, 8, 9, 4, 5, 2, 5, 7, 4, 2, 3, 9, 9, 5, 0, 8, 2, 9, 6, 5, 9, 1, 2, 2, 8, 5, 0, 8, 5, 5, 5, 8, 2, 1, 5, 7, 2, 5, 0, 3, 1, 0, 7, 1, 2, 5, 7, 0,
+        1, 2, 6, 6, 8, 3, 0, 2, 4, 0, 2, 9, 2, 9, 5, 2, 5, 2, 2, 0, 1, 1, 8, 7, 2, 6, 7, 6, 7, 5, 6, 2, 2, 0, 4, 1, 5, 4, 2, 0, 5, 1, 6, 1, 8, 4, 1, 6, 3, 4, 8, 4, 7, 5, 6, 5, 1, 6, 9, 9, 9, 8, 1, 1, 6, 1, 4, 1, 0, 1,
+        0, 0, 2, 9, 9, 6, 0, 7, 8, 3, 8, 6, 9, 0, 9, 2, 9, 1, 6, 0, 3, 0, 2, 8, 8, 4, 0, 0, 2, 6, 9, 1, 0, 4, 1, 4, 0, 7, 9, 2, 8, 8, 6, 2, 1, 5, 0, 7, 8, 4, 2, 4, 5, 1, 6, 7, 0, 9, 0, 8, 7, 0, 0, 0, 6, 9, 9, 2, 8, 2,
+        1, 2, 0, 6, 6, 0, 4, 1, 8, 3, 7, 1, 8, 0, 6, 5, 3, 5, 5, 6, 7, 2, 5, 2, 5, 3, 2, 5, 6, 7, 5, 3, 2, 8, 6, 1, 2, 9, 1, 0, 4, 2, 4, 8, 7, 7, 6, 1, 8, 2, 5, 8, 2, 9, 7, 6, 5, 1, 5, 7, 9, 5, 9, 8, 4, 7, 0, 3, 5, 6,
+        2, 2, 2, 6, 2, 9, 3, 4, 8, 6, 0, 0, 3, 4, 1, 5, 8, 7, 2, 2, 9, 8, 0, 5, 3, 4, 9, 8, 9, 6, 5, 0, 2, 2, 6, 2, 9, 1, 7, 4, 8, 7, 8, 8, 2, 0, 2, 7, 3, 4, 2, 0, 9, 2, 2, 2, 2, 4, 5, 3, 3, 9, 8, 5, 6, 2, 6, 4, 7, 6,
+        6, 9, 1, 4, 9, 0, 5, 5, 6, 2, 8, 4, 2, 5, 0, 3, 9, 1, 2, 7, 5, 7, 7, 1, 0, 2, 8, 4, 0, 2, 7, 9, 9, 8, 0, 6, 6, 3, 6, 5, 8, 2, 5, 4, 8, 8, 9, 2, 6, 4, 8, 8, 0, 2, 5, 4, 5, 6, 6, 1, 0, 1, 7, 2, 9, 6, 7, 0, 2, 6,
+        6, 4, 0, 7, 6, 5, 5, 9, 0, 4, 2, 9, 0, 9, 9, 4, 5, 6, 8, 1, 5, 0, 6, 5, 2, 6, 5, 3, 0, 5, 3, 7, 1, 8, 2, 9, 4, 1, 2, 7, 0, 3, 3, 6, 9, 3, 1, 3, 7, 8, 5, 1, 7, 8, 6, 0, 9, 0, 4, 0, 7, 0, 8, 6, 6, 7, 1, 1, 4, 9,
+        6, 5, 5, 8, 3, 4, 3, 4, 3, 4, 7, 6, 9, 3, 3, 8, 5, 7, 8, 1, 7, 1, 1, 3, 8, 6, 4, 5, 5, 8, 7, 3, 6, 7, 8, 1, 2, 3, 0, 1, 4, 5, 8, 7, 6, 8, 7, 1, 2, 6, 6, 0, 3, 4, 8, 9, 1, 3, 9, 0, 9, 5, 6, 2, 0, 0, 9, 9, 3, 9,
+        3, 6, 1, 0, 3, 1, 0, 2, 9, 1, 6, 1, 6, 1, 5, 2, 8, 8, 1, 3, 8, 4, 3, 7, 9, 0, 9, 9, 0, 4, 2, 3, 1, 7, 4, 7, 3, 3, 6, 3, 9, 4, 8, 0, 4, 5, 7, 5, 9, 3, 1, 4, 9, 3, 1, 4, 0, 5, 2, 9, 7, 6, 3, 4, 7, 5, 7, 4, 8, 1,
+        1, 9, 3, 5, 6, 7, 0, 9, 1, 1, 0, 1, 3, 7, 7, 5, 1, 7, 2, 1, 0, 0, 8, 0, 3, 1, 5, 5, 9, 0, 2, 4, 8, 5, 3, 0, 9, 0, 6, 6, 9, 2, 0, 3, 7, 6, 7, 1, 9, 2, 2, 0, 3, 3, 2, 2, 9, 0, 9, 4, 3, 3, 4, 6, 7, 6, 8, 5, 1, 4,
+        2, 2, 1, 4, 4, 7, 7, 3, 7, 9, 3, 9, 3, 7, 5, 1, 7, 0, 3, 4, 4, 3, 6, 6, 1, 9, 9, 1, 0, 4, 0, 3, 3, 7, 5, 1, 1, 1, 7, 3, 5, 4, 7, 1, 9, 1, 8, 5, 5, 0, 4, 6, 4, 4, 9, 0, 2, 6, 3, 6, 5, 5, 1, 2, 8, 1, 6, 2, 2, 8,
+        8, 2, 4, 4, 6, 2, 5, 7, 5, 9, 1, 6, 3, 3, 3, 0, 3, 9, 1, 0, 7, 2, 2, 5, 3, 8, 3, 7, 4, 2, 1, 8, 2, 1, 4, 0, 8, 8, 3, 5, 0, 8, 6, 5, 7, 3, 9, 1, 7, 7, 1, 5, 0, 9, 6, 8, 2, 8, 8, 7, 4, 7, 8, 2, 6, 5, 6, 9, 9, 5,
+        9, 9, 5, 7, 4, 4, 9, 0, 6, 6, 1, 7, 5, 8, 3, 4, 4, 1, 3, 7, 5, 2, 2, 3, 9, 7, 0, 9, 6, 8, 3, 4, 0, 8, 0, 0, 5, 3, 5, 5, 9, 8, 4, 9, 1, 7, 5, 4, 1, 7, 3, 8, 1, 8, 8, 3, 9, 9, 9, 4, 4, 6, 9, 7, 4, 8, 6, 7, 6, 2,
+        6, 5, 5, 1, 6, 5, 8, 2, 7, 6, 5, 8, 4, 8, 3, 5, 8, 8, 4, 5, 3, 1, 4, 2, 7, 7, 5, 6, 8, 7, 9, 0, 0, 2, 9, 0, 9, 5, 1, 7, 0, 2, 8, 3, 5, 2, 9, 7, 1, 6, 3, 4, 4, 5, 6, 2, 1, 2, 9, 6, 4, 0, 4, 3, 5, 2, 3, 1, 1, 7,
+        6, 0, 0, 6, 6, 5, 1, 0, 1, 2, 4, 1, 2, 0, 0, 6, 5, 9, 7, 5, 5, 8, 5, 1, 2, 7, 6, 1, 7, 8, 5, 8, 3, 8, 2, 9, 2, 0, 4, 1, 9, 7, 4, 8, 4, 4, 2, 3, 6, 0, 8, 0, 0, 7, 1, 9, 3, 0, 4, 5, 7, 6, 1, 8, 9, 3, 2, 3, 4, 9,
+        2, 2, 9, 2, 7, 9, 6, 5, 0, 1, 9, 8, 7, 5, 1, 8, 7, 2, 1, 2, 7, 2, 6, 7, 5, 0, 7, 9, 8, 1, 2, 5, 5, 4, 7, 0, 9, 5, 8, 9, 0, 4, 5, 5, 6, 3, 5, 7, 9, 2, 1, 2, 2, 1, 0, 3, 3, 3, 4, 6, 6, 9, 7, 4, 9, 9, 2, 3, 5, 6,
+        3, 0, 2, 5, 4, 9, 4, 7, 8, 0, 2, 4, 9, 0, 1, 1, 4, 1, 9, 5, 2, 1, 2, 3, 8, 2, 8, 1, 5, 3, 0, 9, 1, 1, 4, 0, 7, 9, 0, 7, 3, 8, 6, 0, 2, 5, 1, 5, 2, 2, 7, 4, 2, 9, 9, 5, 8, 1, 8, 0, 7, 2, 4, 7, 1, 6, 2, 5, 9, 1,
+        6, 6, 8, 5, 4, 5, 1, 3, 3, 3, 1, 2, 3, 9, 4, 8, 0, 4, 9, 4, 7, 0, 7, 9, 1, 1, 9, 1, 5, 3, 2, 6, 7, 3, 4, 3, 0, 2, 8, 2, 4, 4, 1, 8, 6, 0, 4, 1, 4, 2, 6, 3, 6, 3, 9, 5, 4, 8, 0, 0, 0, 4, 4, 8, 0, 0, 2, 6, 7, 0,
+        4, 9, 6, 2, 4, 8, 2, 0, 1, 7, 9, 2, 8, 9, 6, 4, 7, 6, 6, 9, 7, 5, 8, 3, 1, 8, 3, 2, 7, 1, 3, 1, 4, 2, 5, 1, 7, 0, 2, 9, 6, 9, 2, 3, 4, 8, 8, 9, 6, 2, 7, 6, 6, 8, 4, 4, 0, 3, 2, 3, 2, 6, 0, 9, 2, 7, 5, 2, 4, 9,
+        6, 0, 3, 5, 7, 9, 9, 6, 4, 6, 9, 2, 5, 6, 5, 0, 4, 9, 3, 6, 8, 1, 8, 3, 6, 0, 9, 0, 0, 3, 2, 3, 8, 0, 9, 2, 9, 3, 4, 5,
+        9, 5, 8, 8, 9, 7, 0, 6, 9, 5, 3, 6, 5, 3, 4, 9, 4, 0, 6, 0, 3, 4, 0, 2, 1, 6, 6, 5, 4, 4, 3, 7, 5, 5, 8, 9, 0, 0, 4, 5, 6, 3, 2, 8, 8, 2, 2, 5, 0, 5, 4, 5, 2, 5, 5, 6, 4, 0, 5, 6, 4, 4, 8, 2, 4, 6, 5, 1, 5, 1,
+        8, 7, 5, 4, 7, 1, 1, 9, 6, 2, 1, 8, 4, 4, 3, 9, 6, 5, 8, 2, 5, 3, 3, 7, 5, 4, 3, 8, 8, 5, 6, 9, 0, 9, 4, 1, 1, 3, 0, 3, 1, 5, 0, 9, 5, 2, 6, 1, 7, 9, 3, 7, 8, 0, 0, 2, 9, 7, 4, 1, 2, 0, 7, 6, 6, 5, 1, 4, 7, 9,
+        3, 9, 4, 2, 5, 9, 0, 2, 9, 8, 9, 6, 9, 5, 9, 4, 6, 9, 9, 5, 5, 6, 5, 7, 6, 1, 2, 1, 8, 6, 5, 6, 1, 9, 6, 7, 3, 3, 7, 8, 6, 2, 3, 6, 2, 5, 6, 1, 2, 5, 2, 1, 6, 3, 2, 0, 8, 6, 2, 8, 6, 9, 2, 2, 2, 1, 0, 3, 2, 7,
+        4, 8, 8, 9, 2, 1, 8, 6, 5, 4, 3, 6, 4, 8, 0, 2, 2, 9, 6, 7, 8, 0, 7, 0, 5, 7, 6, 5, 6, 1, 5, 1, 4, 4, 6, 3, 2, 0, 4, 6, 9, 2, 7, 9, 0, 6, 8, 2, 1, 2, 0, 7, 3, 8, 8, 3, 7, 7, 8, 1, 4, 2, 3, 3, 5, 6, 2, 8, 2, 3,
+        6, 0, 8, 9, 6, 3, 2, 0, 8, 0, 6, 8, 2, 2, 2, 4, 6, 8, 0, 1, 2, 2, 4, 8, 2, 6, 1, 1, 7, 7, 1, 8, 5, 8, 9, 6, 3, 8, 1, 4, 0, 9, 1, 8, 3, 9, 0, 3, 6, 7, 3, 6, 7, 2, 2, 2, 0, 8, 8, 8, 3, 2, 1, 5, 1, 3, 7, 5, 5, 6,
+        0, 0, 3, 7, 2, 7, 9, 8, 3, 9, 4, 0, 0, 4, 1, 5, 2, 9, 7, 0, 0, 2, 8, 7, 8, 3, 0, 7, 6, 6, 7, 0, 9, 4, 4, 4, 7, 4, 5, 6, 0, 1, 3, 4, 5, 5, 6, 4, 1, 7, 2, 5, 4, 3, 7, 0, 9, 0, 6, 9, 7, 9, 3, 9, 6, 1, 2, 2, 5, 7,
+        1, 4, 2, 9, 8, 9, 4, 6, 7, 1, 5, 4, 3, 5, 7, 8, 4, 6, 8, 7, 8, 8, 6, 1, 4, 4, 4, 5, 8, 1, 2, 3, 1, 4, 5, 9, 3, 5, 7, 1, 9, 8, 4, 9, 2, 2, 5, 2, 8, 4, 7, 1, 6, 0, 5, 0, 4, 9, 2, 2, 1, 2, 4, 2, 4, 7, 0, 1, 4, 1,
+        2, 1, 4, 7, 8, 0, 5, 7, 3, 4, 5, 5, 1, 0, 5, 0, 0, 8, 0, 1, 9, 0, 8, 6, 9, 9, 6, 0, 3, 3, 0, 2, 7, 6, 3, 4, 7, 8, 7, 0, 8, 1, 0, 8, 1, 7, 5, 4, 5, 0, 1, 1, 9, 3, 0, 7, 1, 4, 1, 2, 2, 3, 3, 9, 0, 8, 6, 6, 3, 9,
+        3, 8, 3, 3, 9, 5, 2, 9, 4, 2, 5, 7, 8, 6, 9, 0, 5, 0, 7, 6, 4, 3, 1, 0, 0, 6, 3, 8, 3, 5, 1, 9, 8, 3, 4, 3, 8, 9, 3, 4, 1, 5, 9, 6, 1, 3, 1, 8, 5, 4, 3, 4, 7, 5, 4, 6, 4, 9, 5, 5, 6, 9, 7, 8, 1, 0, 3, 8, 2, 9,
+        3, 0, 9, 7, 1, 6, 4, 6, 5, 1, 4, 3, 8, 4, 0, 7, 0, 0, 7, 0, 7, 3, 6, 0, 4, 1, 1, 2, 3, 7, 3, 5, 9, 9, 8, 4, 3, 4, 5, 2, 2, 5, 1, 6, 1, 0, 5, 0, 7, 0, 2, 7, 0, 5, 6, 2, 3, 5, 2, 6, 6, 0, 1, 2, 7, 6, 4, 8, 4, 8,
+        3, 0, 8, 4, 0, 7, 6, 1, 1, 8, 3, 0, 1, 3, 0, 5, 2, 7, 9, 3, 2, 0, 5, 4, 2, 7, 4, 6, 2, 8, 6, 5, 4, 0, 3, 6, 0, 3, 6, 7, 4, 5, 3, 2, 8, 6, 5, 1, 0, 5, 7, 0, 6, 5, 8, 7, 4, 8, 8, 2, 2, 5, 6, 9, 8, 1, 5, 7, 9, 3,
+        6, 7, 8, 9, 7, 6, 6, 9, 7, 4, 2, 2, 0, 5, 7, 5, 0, 5, 9, 6, 8, 3, 4, 4, 0, 8, 6, 9, 7, 3, 5, 0, 2, 0, 1, 4, 1, 0, 2, 0, 6, 7, 2, 3, 5, 8, 5, 0, 2, 0, 0, 7, 2, 4, 5, 2, 2, 5, 6, 3, 2, 6, 5, 1, 3, 4, 1, 0, 5, 5,
+        9, 2, 4, 0, 1, 9, 0, 2, 7, 4, 2, 1, 6, 2, 4, 8, 4, 3, 9, 1, 4, 0, 3, 5, 9, 9, 8, 9, 5, 3, 5, 3, 9, 4, 5, 9, 0, 9, 4, 4, 0, 7, 0, 4, 6, 9, 1, 2, 0, 9, 1, 4, 0, 9, 3, 8, 7, 0, 0, 1, 2, 6, 4, 5, 6, 0, 0, 1, 6, 2,
+        3, 7, 4, 2, 8, 8, 0, 2, 1, 0, 9, 2, 7, 6, 4, 5, 7, 9, 3, 1, 0, 6, 5, 7, 9, 2, 2, 9, 5, 5, 2, 4, 9, 8, 8, 7, 2, 7, 5, 8, 4, 6, 1, 0, 1, 2, 6, 4, 8, 3, 6, 9, 9, 9, 8, 9, 2, 2, 5, 6, 9, 5, 9, 6, 8, 8, 1, 5, 9, 2,
+        0, 5, 6, 0, 0, 1, 0, 1, 6, 5, 5, 2, 5, 6, 3, 7, 5, 6, 7, 8
+    };
+}
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
new file mode 100644
index 0000000..b7c2d1f
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MessageQueueTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.MediumTest;
+import junit.framework.TestCase;
+
+public class MessageQueueTest extends TestCase {
+
+    private static class BaseTestHandler extends TestHandlerThread {
+        Handler mHandler;
+        int mLastMessage;
+        int mCount;
+
+        public BaseTestHandler() {
+        }
+
+        public void go() {
+            mHandler = new Handler() {
+                public void handleMessage(Message msg) {
+                    BaseTestHandler.this.handleMessage(msg);
+                }
+            };
+        }
+
+        public void handleMessage(Message msg) {
+            if (mCount <= mLastMessage) {
+                if (msg.what != mCount) {
+                    failure(new RuntimeException(
+                            "Expected message #" + mCount
+                                    + ", received #" + msg.what));
+                } else if (mCount == mLastMessage) {
+                    success();
+                }
+                mCount++;
+            } else {
+                failure(new RuntimeException(
+                        "Message received after done, #" + msg.what));
+            }
+        }
+    }
+
+    @MediumTest
+    public void testMessageOrder() throws Exception {
+        TestHandlerThread tester = new BaseTestHandler() {
+            public void go() {
+                super.go();
+                long now = SystemClock.uptimeMillis() + 200;
+                mLastMessage = 4;
+                mCount = 0;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(2), now + 1);
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now + 2);
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(4), now + 2);
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(0), now + 0);
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(1), now + 0);
+            }
+        };
+
+        tester.doTest(1000);
+    }
+
+    @MediumTest
+    public void testAtFrontOfQueue() throws Exception {
+        TestHandlerThread tester = new BaseTestHandler() {
+            public void go() {
+                super.go();
+                long now = SystemClock.uptimeMillis() + 200;
+                mLastMessage = 3;
+                mCount = 0;
+                mHandler.sendMessageAtTime(mHandler.obtainMessage(3), now);
+                mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(2));
+                mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(0));
+            }
+
+            public void handleMessage(Message msg) {
+                super.handleMessage(msg);
+                if (msg.what == 0) {
+                    mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(1));
+                }
+            }
+        };
+
+        tester.doTest(1000);
+    }
+}
+
diff --git a/core/tests/coretests/src/android/os/MessengerService.java b/core/tests/coretests/src/android/os/MessengerService.java
new file mode 100644
index 0000000..f15e134
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MessengerService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+
+public class MessengerService extends Service {
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            Message reply = Message.obtain();
+            reply.copyFrom(msg);
+            try {
+                msg.replyTo.send(reply);
+            } catch (RemoteException e) {
+            }
+        }
+    };
+    
+    private final Messenger mMessenger = new Messenger(mHandler);
+    
+    public MessengerService() {
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mMessenger.getBinder();
+    }
+}
+
diff --git a/core/tests/coretests/src/android/os/MessengerTest.java b/core/tests/coretests/src/android/os/MessengerTest.java
new file mode 100644
index 0000000..473ffe2
--- /dev/null
+++ b/core/tests/coretests/src/android/os/MessengerTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+public class MessengerTest extends AndroidTestCase {
+    private Messenger mServiceMessenger;
+    
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (MessengerTest.this) {
+                mServiceMessenger = new Messenger(service);
+                MessengerTest.this.notifyAll();
+            }
+        }
+        public void onServiceDisconnected(ComponentName name) {
+            mServiceMessenger = null;
+        }
+    };
+    
+    private class TestThread extends TestHandlerThread {
+        private Handler mTestHandler;
+        private Messenger mTestMessenger;
+        
+        public void go() {
+            synchronized (MessengerTest.this) {
+                mTestHandler = new Handler() {
+                    public void handleMessage(Message msg) {
+                        TestThread.this.handleMessage(msg);
+                    }
+                };
+                mTestMessenger = new Messenger(mTestHandler);
+                TestThread.this.executeTest();
+            }
+        }
+
+        public void executeTest() {
+            Message msg = Message.obtain();
+            msg.arg1 = 100;
+            msg.arg2 = 1000;
+            msg.replyTo = mTestMessenger;
+            try {
+                mServiceMessenger.send(msg);
+            } catch (RemoteException e) {
+            }
+        }
+        
+        public void handleMessage(Message msg) {
+            if (msg.arg1 != 100) {
+                failure(new RuntimeException(
+                        "Message.arg1 is not 100: " + msg.arg1));
+                return;
+            }
+            if (msg.arg2 != 1000) {
+                failure(new RuntimeException(
+                        "Message.arg2 is not 1000: " + msg.arg2));
+                return;
+            }
+            if (!mTestMessenger.equals(msg.replyTo)) {
+                failure(new RuntimeException(
+                        "Message.replyTo is not me: " + msg.replyTo));
+                return;
+            }
+            success();
+        }
+    };
+    
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getContext().bindService(new Intent(mContext, MessengerService.class),
+                mConnection, Context.BIND_AUTO_CREATE);
+        synchronized (this) {
+            while (mServiceMessenger == null) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        getContext().unbindService(mConnection);
+    }
+
+    @MediumTest
+    public void testSend() {
+        (new TestThread()).doTest(1000);
+        
+    }
+}
diff --git a/core/tests/coretests/src/android/os/OsTests.java b/core/tests/coretests/src/android/os/OsTests.java
new file mode 100644
index 0000000..582bf1a
--- /dev/null
+++ b/core/tests/coretests/src/android/os/OsTests.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import com.google.android.collect.Lists;
+import junit.framework.TestSuite;
+
+import java.util.Enumeration;
+import java.util.List;
+
+public class OsTests {
+    public static TestSuite suite() {
+        TestSuite suite = new TestSuite(OsTests.class.getName());
+
+        suite.addTestSuite(AidlTest.class);
+        suite.addTestSuite(BroadcasterTest.class);
+        suite.addTestSuite(FileObserverTest.class);
+        suite.addTestSuite(IdleHandlerTest.class);
+        suite.addTestSuite(MessageQueueTest.class);
+        suite.addTestSuite(MessengerTest.class);
+        suite.addTestSuite(SystemPropertiesTest.class);
+
+        return suite;
+    }
+}
diff --git a/core/tests/coretests/src/android/os/PerformanceCollectorTest.java b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
new file mode 100644
index 0000000..a382239
--- /dev/null
+++ b/core/tests/coretests/src/android/os/PerformanceCollectorTest.java
@@ -0,0 +1,528 @@
+/*
+ * 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.os;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.PerformanceCollector;
+import android.os.Process;
+import android.os.PerformanceCollector.PerformanceResultsWriter;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+public class PerformanceCollectorTest extends TestCase {
+
+    private PerformanceCollector mPerfCollector;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mPerfCollector = new PerformanceCollector();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mPerfCollector = null;
+    }
+
+    @SmallTest
+    public void testBeginSnapshotNoWriter() throws Exception {
+        mPerfCollector.beginSnapshot("testBeginSnapshotNoWriter");
+
+        assertTrue((Long)readPrivateField("mSnapshotCpuTime", mPerfCollector) > 0);
+        assertTrue((Long)readPrivateField("mSnapshotExecTime", mPerfCollector) > 0);
+        Bundle snapshot = (Bundle)readPrivateField("mPerfSnapshot", mPerfCollector);
+        assertNotNull(snapshot);
+        assertEquals(2, snapshot.size());
+    }
+
+    @SmallTest
+    public void testEndSnapshotNoWriter() throws Exception {
+        mPerfCollector.beginSnapshot("testEndSnapshotNoWriter");
+        workForRandomLongPeriod();
+        Bundle snapshot = mPerfCollector.endSnapshot();
+
+        verifySnapshotBundle(snapshot);
+    }
+
+    @SmallTest
+    public void testStartTimingNoWriter() throws Exception {
+        mPerfCollector.startTiming("testStartTimingNoWriter");
+
+        assertTrue((Long)readPrivateField("mCpuTime", mPerfCollector) > 0);
+        assertTrue((Long)readPrivateField("mExecTime", mPerfCollector) > 0);
+        Bundle measurement = (Bundle)readPrivateField("mPerfMeasurement", mPerfCollector);
+        assertNotNull(measurement);
+        verifyTimingBundle(measurement, new ArrayList<String>());
+    }
+
+    @SmallTest
+    public void testAddIterationNoWriter() throws Exception {
+        mPerfCollector.startTiming("testAddIterationNoWriter");
+        workForRandomTinyPeriod();
+        Bundle iteration = mPerfCollector.addIteration("timing1");
+
+        verifyIterationBundle(iteration, "timing1");
+    }
+
+    @SmallTest
+    public void testStopTimingNoWriter() throws Exception {
+        mPerfCollector.startTiming("testStopTimingNoWriter");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("timing2");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("timing3");
+        workForRandomShortPeriod();
+        Bundle timing = mPerfCollector.stopTiming("timing4");
+
+        ArrayList<String> labels = new ArrayList<String>();
+        labels.add("timing2");
+        labels.add("timing3");
+        labels.add("timing4");
+        verifyTimingBundle(timing, labels);
+    }
+
+    @SmallTest
+    public void testBeginSnapshot() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.beginSnapshot("testBeginSnapshot");
+
+        assertEquals("testBeginSnapshot", writer.snapshotLabel);
+        assertTrue((Long)readPrivateField("mSnapshotCpuTime", mPerfCollector) > 0);
+        assertTrue((Long)readPrivateField("mSnapshotExecTime", mPerfCollector) > 0);
+        Bundle snapshot = (Bundle)readPrivateField("mPerfSnapshot", mPerfCollector);
+        assertNotNull(snapshot);
+        assertEquals(2, snapshot.size());
+    }
+
+    @SmallTest
+    public void testEndSnapshot() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.beginSnapshot("testEndSnapshot");
+        workForRandomLongPeriod();
+        Bundle snapshot1 = mPerfCollector.endSnapshot();
+        Bundle snapshot2 = writer.snapshotResults;
+
+        assertEqualsBundle(snapshot1, snapshot2);
+        verifySnapshotBundle(snapshot1);
+    }
+
+    @SmallTest
+    public void testStartTiming() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.startTiming("testStartTiming");
+
+        assertEquals("testStartTiming", writer.timingLabel);
+        assertTrue((Long)readPrivateField("mCpuTime", mPerfCollector) > 0);
+        assertTrue((Long)readPrivateField("mExecTime", mPerfCollector) > 0);
+        Bundle measurement = (Bundle)readPrivateField("mPerfMeasurement", mPerfCollector);
+        assertNotNull(measurement);
+        verifyTimingBundle(measurement, new ArrayList<String>());
+    }
+
+    @SmallTest
+    public void testAddIteration() throws Exception {
+        mPerfCollector.startTiming("testAddIteration");
+        workForRandomTinyPeriod();
+        Bundle iteration = mPerfCollector.addIteration("timing5");
+
+        verifyIterationBundle(iteration, "timing5");
+    }
+
+    @SmallTest
+    public void testStopTiming() throws Exception {
+        mPerfCollector.startTiming("testStopTiming");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("timing6");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("timing7");
+        workForRandomShortPeriod();
+        Bundle timing = mPerfCollector.stopTiming("timing8");
+
+        ArrayList<String> labels = new ArrayList<String>();
+        labels.add("timing6");
+        labels.add("timing7");
+        labels.add("timing8");
+        verifyTimingBundle(timing, labels);
+    }
+
+    @SmallTest
+    public void testAddMeasurementLong() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.startTiming("testAddMeasurementLong");
+        mPerfCollector.addMeasurement("testAddMeasurementLongZero", 0);
+        mPerfCollector.addMeasurement("testAddMeasurementLongPos", 348573);
+        mPerfCollector.addMeasurement("testAddMeasurementLongNeg", -19354);
+        mPerfCollector.stopTiming("");
+
+        assertEquals("testAddMeasurementLong", writer.timingLabel);
+        Bundle results = writer.timingResults;
+        assertEquals(4, results.size());
+        assertTrue(results.containsKey("testAddMeasurementLongZero"));
+        assertEquals(0, results.getLong("testAddMeasurementLongZero"));
+        assertTrue(results.containsKey("testAddMeasurementLongPos"));
+        assertEquals(348573, results.getLong("testAddMeasurementLongPos"));
+        assertTrue(results.containsKey("testAddMeasurementLongNeg"));
+        assertEquals(-19354, results.getLong("testAddMeasurementLongNeg"));
+    }
+
+    @SmallTest
+    public void testAddMeasurementFloat() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.startTiming("testAddMeasurementFloat");
+        mPerfCollector.addMeasurement("testAddMeasurementFloatZero", 0.0f);
+        mPerfCollector.addMeasurement("testAddMeasurementFloatPos", 348573.345f);
+        mPerfCollector.addMeasurement("testAddMeasurementFloatNeg", -19354.093f);
+        mPerfCollector.stopTiming("");
+
+        assertEquals("testAddMeasurementFloat", writer.timingLabel);
+        Bundle results = writer.timingResults;
+        assertEquals(4, results.size());
+        assertTrue(results.containsKey("testAddMeasurementFloatZero"));
+        assertEquals(0.0f, results.getFloat("testAddMeasurementFloatZero"));
+        assertTrue(results.containsKey("testAddMeasurementFloatPos"));
+        assertEquals(348573.345f, results.getFloat("testAddMeasurementFloatPos"));
+        assertTrue(results.containsKey("testAddMeasurementFloatNeg"));
+        assertEquals(-19354.093f, results.getFloat("testAddMeasurementFloatNeg"));
+    }
+
+    @SmallTest
+    public void testAddMeasurementString() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.startTiming("testAddMeasurementString");
+        mPerfCollector.addMeasurement("testAddMeasurementStringNull", null);
+        mPerfCollector.addMeasurement("testAddMeasurementStringEmpty", "");
+        mPerfCollector.addMeasurement("testAddMeasurementStringNonEmpty", "Hello World");
+        mPerfCollector.stopTiming("");
+
+        assertEquals("testAddMeasurementString", writer.timingLabel);
+        Bundle results = writer.timingResults;
+        assertEquals(4, results.size());
+        assertTrue(results.containsKey("testAddMeasurementStringNull"));
+        assertNull(results.getString("testAddMeasurementStringNull"));
+        assertTrue(results.containsKey("testAddMeasurementStringEmpty"));
+        assertEquals("", results.getString("testAddMeasurementStringEmpty"));
+        assertTrue(results.containsKey("testAddMeasurementStringNonEmpty"));
+        assertEquals("Hello World", results.getString("testAddMeasurementStringNonEmpty"));
+    }
+
+    @SmallTest
+    public void testSimpleSequence() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.beginSnapshot("testSimpleSequence");
+        mPerfCollector.startTiming("testSimpleSequenceTiming");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration1");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration2");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration3");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration4");
+        workForRandomShortPeriod();
+        Bundle timing = mPerfCollector.stopTiming("iteration5");
+        workForRandomLongPeriod();
+        Bundle snapshot1 = mPerfCollector.endSnapshot();
+        Bundle snapshot2 = writer.snapshotResults;
+
+        assertEqualsBundle(snapshot1, snapshot2);
+        verifySnapshotBundle(snapshot1);
+
+        ArrayList<String> labels = new ArrayList<String>();
+        labels.add("iteration1");
+        labels.add("iteration2");
+        labels.add("iteration3");
+        labels.add("iteration4");
+        labels.add("iteration5");
+        verifyTimingBundle(timing, labels);
+    }
+
+    @SmallTest
+    public void testLongSequence() throws Exception {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.beginSnapshot("testLongSequence");
+        mPerfCollector.startTiming("testLongSequenceTiming1");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration1");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration2");
+        workForRandomShortPeriod();
+        Bundle timing1 = mPerfCollector.stopTiming("iteration3");
+        workForRandomLongPeriod();
+
+        mPerfCollector.startTiming("testLongSequenceTiming2");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration4");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration5");
+        workForRandomShortPeriod();
+        Bundle timing2 = mPerfCollector.stopTiming("iteration6");
+        workForRandomLongPeriod();
+
+        mPerfCollector.startTiming("testLongSequenceTiming3");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration7");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration8");
+        workForRandomShortPeriod();
+        Bundle timing3 = mPerfCollector.stopTiming("iteration9");
+        workForRandomLongPeriod();
+
+        mPerfCollector.startTiming("testLongSequenceTiming4");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration10");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration11");
+        workForRandomShortPeriod();
+        Bundle timing4 = mPerfCollector.stopTiming("iteration12");
+        workForRandomLongPeriod();
+
+        mPerfCollector.startTiming("testLongSequenceTiming5");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration13");
+        workForRandomTinyPeriod();
+        mPerfCollector.addIteration("iteration14");
+        workForRandomShortPeriod();
+        Bundle timing5 = mPerfCollector.stopTiming("iteration15");
+        workForRandomLongPeriod();
+        Bundle snapshot1 = mPerfCollector.endSnapshot();
+        Bundle snapshot2 = writer.snapshotResults;
+
+        assertEqualsBundle(snapshot1, snapshot2);
+        verifySnapshotBundle(snapshot1);
+
+        ArrayList<String> labels1 = new ArrayList<String>();
+        labels1.add("iteration1");
+        labels1.add("iteration2");
+        labels1.add("iteration3");
+        verifyTimingBundle(timing1, labels1);
+        ArrayList<String> labels2 = new ArrayList<String>();
+        labels2.add("iteration4");
+        labels2.add("iteration5");
+        labels2.add("iteration6");
+        verifyTimingBundle(timing2, labels2);
+        ArrayList<String> labels3 = new ArrayList<String>();
+        labels3.add("iteration7");
+        labels3.add("iteration8");
+        labels3.add("iteration9");
+        verifyTimingBundle(timing3, labels3);
+        ArrayList<String> labels4 = new ArrayList<String>();
+        labels4.add("iteration10");
+        labels4.add("iteration11");
+        labels4.add("iteration12");
+        verifyTimingBundle(timing4, labels4);
+        ArrayList<String> labels5 = new ArrayList<String>();
+        labels5.add("iteration13");
+        labels5.add("iteration14");
+        labels5.add("iteration15");
+        verifyTimingBundle(timing5, labels5);
+    }
+
+    /*
+     * Verify that snapshotting and timing do not interfere w/ each other,
+     * by staggering calls to snapshot and timing functions.
+     */
+    @SmallTest
+    public void testOutOfOrderSequence() {
+        MockPerformanceResultsWriter writer = new MockPerformanceResultsWriter();
+        mPerfCollector.setPerformanceResultsWriter(writer);
+        mPerfCollector.startTiming("testOutOfOrderSequenceTiming");
+        workForRandomShortPeriod();
+        mPerfCollector.beginSnapshot("testOutOfOrderSequenceSnapshot");
+        workForRandomShortPeriod();
+        Bundle timing1 = mPerfCollector.stopTiming("timing1");
+        workForRandomShortPeriod();
+        Bundle snapshot1 = mPerfCollector.endSnapshot();
+
+        Bundle timing2 = writer.timingResults;
+        Bundle snapshot2 = writer.snapshotResults;
+
+        assertEqualsBundle(snapshot1, snapshot2);
+        verifySnapshotBundle(snapshot1);
+
+        assertEqualsBundle(timing1, timing2);
+        ArrayList<String> labels = new ArrayList<String>();
+        labels.add("timing1");
+        verifyTimingBundle(timing1, labels);
+    }
+
+    private void workForRandomPeriod(int minDuration, int maxDuration) {
+        Random random = new Random();
+        int period = minDuration + random.nextInt(maxDuration - minDuration);
+        long start = Process.getElapsedCpuTime();
+        // Generate positive amount of work, so cpu time is measurable in
+        // milliseconds
+        while (Process.getElapsedCpuTime() - start < period) {
+            for (int i = 0, temp = 0; i < 50; i++ ) {
+                temp += i;
+            }
+        }
+    }
+
+    private void workForRandomTinyPeriod() {
+        workForRandomPeriod(2, 5);
+    }
+
+    private void workForRandomShortPeriod() {
+        workForRandomPeriod(10, 25);
+    }
+
+    private void workForRandomLongPeriod() {
+        workForRandomPeriod(50, 100);
+    }
+
+    private void verifySnapshotBundle(Bundle snapshot) {
+        assertTrue("At least 26 metrics collected", 26 <= snapshot.size());
+
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_CPU_TIME));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_EXECUTION_TIME));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME) > 0);
+
+        assertTrue(snapshot.containsKey(
+                PerformanceCollector.METRIC_KEY_PRE_RECEIVED_TRANSACTIONS));
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_PRE_SENT_TRANSACTIONS));
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_RECEIVED_TRANSACTIONS));
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_SENT_TRANSACTIONS));
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GC_INVOCATION_COUNT));
+
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_ALLOCATED));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_ALLOCATED) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_FREE));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_FREE) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_PRIVATE_DIRTY));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_PRIVATE_DIRTY) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_PSS));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_PSS) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_SHARED_DIRTY));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_SHARED_DIRTY) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_JAVA_SIZE));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_JAVA_SIZE) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_ALLOCATED));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_ALLOCATED) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_FREE));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_FREE) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_PRIVATE_DIRTY));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_PRIVATE_DIRTY) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_PSS));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_PSS) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_SHARED_DIRTY));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_SHARED_DIRTY) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_NATIVE_SIZE));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_NATIVE_SIZE) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_COUNT));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_COUNT) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_SIZE));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_ALLOC_SIZE) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_COUNT));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_COUNT) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_SIZE));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_GLOBAL_FREED_SIZE) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_PRIVATE_DIRTY));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_PRIVATE_DIRTY) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_PSS));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_PSS) > 0);
+        assertTrue(snapshot.containsKey(PerformanceCollector.METRIC_KEY_OTHER_SHARED_DIRTY));
+        assertTrue(snapshot.getLong(PerformanceCollector.METRIC_KEY_OTHER_SHARED_DIRTY) > 0);
+    }
+
+    private void verifyIterationBundle(Bundle iteration, String label) {
+        assertEquals(3, iteration.size());
+        assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_LABEL));
+        assertEquals(label, iteration.getString(PerformanceCollector.METRIC_KEY_LABEL));
+        assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_CPU_TIME));
+        assertTrue(iteration.getLong(PerformanceCollector.METRIC_KEY_CPU_TIME) > 0);
+        assertTrue(iteration.containsKey(PerformanceCollector.METRIC_KEY_EXECUTION_TIME));
+        assertTrue(iteration.getLong(PerformanceCollector.METRIC_KEY_EXECUTION_TIME) > 0);
+    }
+
+    private void verifyTimingBundle(Bundle timing, ArrayList<String> labels) {
+        assertEquals(1, timing.size());
+        assertTrue(timing.containsKey(PerformanceCollector.METRIC_KEY_ITERATIONS));
+        ArrayList<Parcelable> iterations = timing.getParcelableArrayList(
+                PerformanceCollector.METRIC_KEY_ITERATIONS);
+        assertNotNull(iterations);
+        assertEquals(labels.size(), iterations.size());
+        for (int i = 0; i < labels.size(); i ++) {
+            Bundle iteration = (Bundle)iterations.get(i);
+            verifyIterationBundle(iteration, labels.get(i));
+        }
+    }
+
+    private void assertEqualsBundle(Bundle b1, Bundle b2) {
+        assertEquals(b1.keySet(), b2.keySet());
+        for (String key : b1.keySet()) {
+            assertEquals(b1.get(key), b2.get(key));
+        }
+    }
+
+    private Object readPrivateField(String fieldName, Object object) throws Exception {
+        Field f = object.getClass().getDeclaredField(fieldName);
+        f.setAccessible(true);
+        return f.get(object);
+    }
+
+    private class MockPerformanceResultsWriter implements PerformanceResultsWriter {
+
+        public String snapshotLabel;
+        public Bundle snapshotResults = new Bundle();
+        public String timingLabel;
+        public Bundle timingResults = new Bundle();
+
+        public void writeBeginSnapshot(String label) {
+            snapshotLabel = label;
+        }
+
+        public void writeEndSnapshot(Bundle results) {
+            snapshotResults.putAll(results);
+        }
+
+        public void writeStartTiming(String label) {
+            timingLabel = label;
+        }
+
+        public void writeStopTiming(Bundle results) {
+            timingResults.putAll(results);
+        }
+
+        public void writeMeasurement(String label, long value) {
+            timingResults.putLong(label, value);
+        }
+
+        public void writeMeasurement(String label, float value) {
+            timingResults.putFloat(label, value);
+        }
+
+        public void writeMeasurement(String label, String value) {
+            timingResults.putString(label, value);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
new file mode 100644
index 0000000..e089b3e
--- /dev/null
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.os;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+public class PowerManagerTest extends AndroidTestCase {
+    
+    private PowerManager mPm;
+    
+    /**
+     * Setup any common data for the upcoming tests.
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mPm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+    }
+    
+    /**
+     * Confirm that the setup is good.
+     * 
+     * @throws Exception
+     */
+    @MediumTest
+    public void testPreconditions() throws Exception {
+        assertNotNull(mPm);
+    }
+
+    /**
+     * Confirm that we can create functional wakelocks.
+     * 
+     * @throws Exception
+     */
+    @MediumTest
+    public void testNewWakeLock() throws Exception {
+        PowerManager.WakeLock wl = mPm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "FULL_WAKE_LOCK");
+        doTestWakeLock(wl);
+
+        wl = mPm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "SCREEN_BRIGHT_WAKE_LOCK");
+        doTestWakeLock(wl);
+
+        wl = mPm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "SCREEN_DIM_WAKE_LOCK");
+        doTestWakeLock(wl);
+
+        wl = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PARTIAL_WAKE_LOCK");
+        doTestWakeLock(wl);
+        
+        doTestSetBacklightBrightness();
+
+        // TODO: Some sort of functional test (maybe not in the unit test here?) 
+        // that confirms that things are really happening e.g. screen power, keyboard power.
+}
+    
+    /**
+     * Confirm that we can't create dysfunctional wakelocks.
+     * 
+     * @throws Exception
+     */
+    @MediumTest
+    public void testBadNewWakeLock() throws Exception {
+        
+        final int badFlags = PowerManager.SCREEN_BRIGHT_WAKE_LOCK 
+                            | PowerManager.SCREEN_DIM_WAKE_LOCK;
+        // wrap in try because we want the error here
+        try {
+            PowerManager.WakeLock wl = mPm.newWakeLock(badFlags, "foo");
+        } catch (IllegalArgumentException e) {
+            return;
+        }
+        fail("Bad WakeLock flag was not caught.");
+    }
+    
+    /**
+     * Apply a few tests to a wakelock to make sure it's healthy.
+     * 
+     * @param wl The wakelock to be tested.
+     */
+    private void doTestWakeLock(PowerManager.WakeLock wl) {
+        // First try simple acquire/release
+        wl.acquire();
+        assertTrue(wl.isHeld());
+        wl.release();
+        assertFalse(wl.isHeld());
+        
+        // Try ref-counted acquire/release
+        wl.setReferenceCounted(true);
+        wl.acquire();
+        assertTrue(wl.isHeld());
+        wl.acquire();
+        assertTrue(wl.isHeld());
+        wl.release();
+        assertTrue(wl.isHeld());
+        wl.release();
+        assertFalse(wl.isHeld());
+        
+        // Try non-ref-counted
+        wl.setReferenceCounted(false);
+        wl.acquire();
+        assertTrue(wl.isHeld());
+        wl.acquire();
+        assertTrue(wl.isHeld());
+        wl.release();
+        assertFalse(wl.isHeld());
+        
+        // TODO: Threaded test (needs handler) to make sure timed wakelocks work too
+    }
+    
+ 
+    /**
+     * Test that calling {@link android.os.IHardwareService#setBacklights(int)} requires
+     * permissions.
+     * <p>Tests permission:
+     *   {@link android.Manifest.permission#DEVICE_POWER}
+     */
+    private void doTestSetBacklightBrightness() {
+        try {
+            mPm.setBacklightBrightness(0);
+            fail("setBacklights did not throw SecurityException as expected");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+}
diff --git a/core/tests/coretests/src/android/os/SystemPropertiesTest.java b/core/tests/coretests/src/android/os/SystemPropertiesTest.java
new file mode 100644
index 0000000..25868ce
--- /dev/null
+++ b/core/tests/coretests/src/android/os/SystemPropertiesTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import static junit.framework.Assert.assertEquals;
+import junit.framework.TestCase;
+
+import android.os.SystemProperties;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class SystemPropertiesTest extends TestCase {
+    private static final String KEY = "com.android.frameworks.coretests";
+    @SmallTest
+    public void testProperties() throws Exception {
+        if (false) {
+        String value;
+       
+        SystemProperties.set(KEY, "");
+        value = SystemProperties.get(KEY, "default");
+        assertEquals("default", value);
+
+        SystemProperties.set(KEY, "AAA");
+        value = SystemProperties.get(KEY, "default");
+        assertEquals("AAA", value);
+
+        value = SystemProperties.get(KEY);
+        assertEquals("AAA", value);
+
+        SystemProperties.set(KEY, "");
+        value = SystemProperties.get(KEY, "default");
+        assertEquals("default", value);
+
+        value = SystemProperties.get(KEY);
+        assertEquals("", value);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/os/TestHandlerThread.java b/core/tests/coretests/src/android/os/TestHandlerThread.java
new file mode 100644
index 0000000..7e84af3
--- /dev/null
+++ b/core/tests/coretests/src/android/os/TestHandlerThread.java
@@ -0,0 +1,104 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue.IdleHandler;
+
+abstract class TestHandlerThread {
+    private boolean mDone = false;
+    private boolean mSuccess = false;
+    private RuntimeException mFailure = null;
+    private Looper mLooper;
+    
+    public abstract void go();
+
+    public TestHandlerThread() {
+    }
+
+    public void doTest(long timeout) {
+        (new LooperThread()).start();
+
+        synchronized (this) {
+            long now = System.currentTimeMillis();
+            long endTime = now + timeout;
+            while (!mDone && now < endTime) {
+                try {
+                    wait(endTime-now);
+                }
+                catch (InterruptedException e) {
+                }
+                now = System.currentTimeMillis();
+            }
+        }
+
+        mLooper.quit();
+
+        if (!mDone) {
+            throw new RuntimeException("test timed out");
+        }
+        if (!mSuccess) {
+            throw mFailure;
+        }
+    }
+
+    public Looper getLooper() {
+        return mLooper;
+    }
+
+    public void success() {
+        synchronized (this) {
+            mSuccess = true;
+            quit();
+        }
+    }
+
+    public void failure(RuntimeException failure) {
+        synchronized (this) {
+            mSuccess = false;
+            mFailure = failure;
+            quit();
+        }
+    }
+
+    class LooperThread extends Thread {
+        public void run() {
+            Looper.prepare();
+            mLooper = Looper.myLooper();
+            go();
+            Looper.loop();
+
+            synchronized (TestHandlerThread.this) {
+                mDone = true;
+                if (!mSuccess && mFailure == null) {
+                    mFailure = new RuntimeException("no failure exception set");
+                }
+                TestHandlerThread.this.notifyAll();
+            }
+        }
+
+    }
+
+    private void quit() {
+        synchronized (this) {
+            mDone = true;
+            notifyAll();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/os/TraceTest.java b/core/tests/coretests/src/android/os/TraceTest.java
new file mode 100644
index 0000000..7a788ee
--- /dev/null
+++ b/core/tests/coretests/src/android/os/TraceTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.os.Debug;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+/**
+ * This class is used to test the native tracing support.  Run this test
+ * while tracing on the emulator and then run traceview to view the trace.
+ */
+public class TraceTest extends AndroidTestCase
+{
+    private static final String TAG = "TraceTest";
+    private int eMethodCalls = 0;
+    private int fMethodCalls = 0;
+    private int gMethodCalls = 0;
+    
+    @SmallTest
+    public void testNativeTracingFromJava()
+    {
+        long start = System.currentTimeMillis();
+        Debug.startNativeTracing();
+        //nativeMethod();
+        int count = 0;
+        for (int ii = 0; ii < 20; ii++) {
+            count = eMethod();
+        }
+        Debug.stopNativeTracing();
+        long end = System.currentTimeMillis();
+        long elapsed = end - start;
+        Log.i(TAG, "elapsed millis: " + elapsed);
+        Log.i(TAG, "eMethod calls: " + eMethodCalls
+                + " fMethod calls: " + fMethodCalls
+                + " gMethod calls: " + gMethodCalls);
+    }
+    
+    // This should not run in the automated suite.
+    @Suppress
+    public void disableTestNativeTracingFromC()
+    {
+        long start = System.currentTimeMillis();
+        nativeMethodAndStartTracing();
+        long end = System.currentTimeMillis();
+        long elapsed = end - start;
+        Log.i(TAG, "elapsed millis: " + elapsed);
+    }
+
+    native void nativeMethod();
+    native void nativeMethodAndStartTracing();
+    
+    @LargeTest
+    public void testMethodTracing()
+    {
+        long start = System.currentTimeMillis();
+        Debug.startMethodTracing("traceTest");
+        topMethod();
+        Debug.stopMethodTracing();
+        long end = System.currentTimeMillis();
+        long elapsed = end - start;
+        Log.i(TAG, "elapsed millis: " + elapsed);
+    }
+    
+    private void topMethod() {
+        aMethod();
+        bMethod();
+        cMethod();
+        dMethod(5);
+        
+        Thread t1 = new aThread();
+        t1.start();
+        Thread t2 = new aThread();
+        t2.start();
+        Thread t3 = new aThread();
+        t3.start();
+        try {
+            t1.join();
+            t2.join();
+            t3.join();
+        } catch (InterruptedException e) {
+        }
+    }
+    
+    private class aThread extends Thread {
+        @Override
+        public void run() {
+            aMethod();
+            bMethod();
+            cMethod();
+        }
+    }
+    
+    /** Calls other methods to make some interesting trace data.
+     * 
+     * @return a meaningless value
+     */
+    private int aMethod() {
+        int count = 0;
+        for (int ii = 0; ii < 6; ii++) {
+            count += bMethod();
+        }
+        for (int ii = 0; ii < 5; ii++) {
+            count += cMethod();
+        }
+        for (int ii = 0; ii < 4; ii++) {
+            count += dMethod(ii);
+        }
+        return count;
+    }
+    
+    /** Calls another method to make some interesting trace data.
+     * 
+     * @return a meaningless value
+     */
+    private int bMethod() {
+        int count = 0;
+        for (int ii = 0; ii < 4; ii++) {
+            count += cMethod();
+        }
+        return count;
+    }
+    
+    /** Executes a simple loop to make some interesting trace data.
+     * 
+     * @return a meaningless value
+     */
+    private int cMethod() {
+        int count = 0;
+        for (int ii = 0; ii < 1000; ii++) {
+            count += ii;
+        }
+        return count;
+    }
+    
+    /** Calls itself recursively to make some interesting trace data.
+     * 
+     * @return a meaningless value
+     */
+    private int dMethod(int level) {
+        int count = 0;
+        if (level > 0) {
+            count = dMethod(level - 1);
+        }
+        for (int ii = 0; ii < 100; ii++) {
+            count += ii;
+        }
+        if (level == 0) {
+            return count;
+        }
+        return dMethod(level - 1);
+    }
+    
+    public int eMethod() {
+        eMethodCalls += 1;
+        int count = fMethod();
+        count += gMethod(3);
+        return count;
+    }
+    
+    public int fMethod() {
+        fMethodCalls += 1;
+        int count = 0;
+        for (int ii = 0; ii < 10; ii++) {
+            count += ii;
+        }
+        return count;
+    }
+    
+    public int gMethod(int level) {
+        gMethodCalls += 1;
+        int count = level;
+        if (level > 1)
+            count += gMethod(level - 1);
+        return count;
+    }
+
+    /*
+     * This causes the native shared library to be loaded when the
+     * class is first used.  The library is only loaded once, even if
+     * multiple classes include this line.
+     *
+     * The library must be in java.library.path, which is derived from
+     * LD_LIBRARY_PATH.  The actual library name searched for will be
+     * "libtrace_test.so" under Linux, but may be different on other
+     * platforms.
+     */
+    static {
+        Log.i(TAG, "Loading trace_test native library...");
+        try {
+            System.loadLibrary("trace_test");
+            Log.i(TAG, "Successfully loaded trace_test native library");
+        }
+        catch (UnsatisfiedLinkError ule) {
+            Log.w(TAG, "Could not load trace_test native library");
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java b/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java
new file mode 100644
index 0000000..b3c0773
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ContentValuesBuilder.java
@@ -0,0 +1,81 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+
+/**
+ * ContentValues-like class which enables users to chain put() methods and restricts
+ * the other methods.
+ */
+/* package */ class ContentValuesBuilder {
+    private final ContentValues mContentValues;
+
+    public ContentValuesBuilder(final ContentValues contentValues) {
+        mContentValues = contentValues;
+    }
+
+    public ContentValuesBuilder put(String key, String value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, Byte value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, Short value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, Integer value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, Long value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, Float value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, Double value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, Boolean value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder put(String key, byte[] value) {
+        mContentValues.put(key, value);
+        return this;
+    }
+
+    public ContentValuesBuilder putNull(String key) {
+        mContentValues.putNull(key);
+        return this;
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java
new file mode 100644
index 0000000..b9e9875
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifier.java
@@ -0,0 +1,101 @@
+/*
+ * 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.pim.vcard;
+
+import android.pim.vcard.VCardConfig;
+import android.pim.vcard.VCardEntry;
+import android.pim.vcard.VCardEntryConstructor;
+import android.pim.vcard.VCardEntryHandler;
+import android.pim.vcard.VCardParser;
+import android.pim.vcard.VCardParser_V21;
+import android.pim.vcard.VCardParser_V30;
+import android.pim.vcard.exception.VCardException;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/* package */ class ContentValuesVerifier implements VCardEntryHandler {
+    private AndroidTestCase mTestCase;
+    private List<ContentValuesVerifierElem> mContentValuesVerifierElemList =
+        new ArrayList<ContentValuesVerifierElem>();
+    private int mIndex;
+
+    public ContentValuesVerifierElem addElem(AndroidTestCase androidTestCase) {
+        mTestCase = androidTestCase;
+        ContentValuesVerifierElem importVerifier = new ContentValuesVerifierElem(androidTestCase);
+        mContentValuesVerifierElemList.add(importVerifier);
+        return importVerifier;
+    }
+
+    public void verify(int resId, int vCardType) throws IOException, VCardException {
+        verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType);
+    }
+
+    public void verify(int resId, int vCardType, final VCardParser vCardParser)
+            throws IOException, VCardException {
+        verify(mTestCase.getContext().getResources().openRawResource(resId),
+                vCardType, vCardParser);
+    }
+
+    public void verify(InputStream is, int vCardType) throws IOException, VCardException {
+        final VCardParser vCardParser;
+        if (VCardConfig.isV30(vCardType)) {
+            vCardParser = new VCardParser_V30(true);  // use StrictParsing
+        } else {
+            vCardParser = new VCardParser_V21();
+        }
+        verify(is, vCardType, vCardParser);
+    }
+
+    public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
+            throws IOException, VCardException {
+        VCardEntryConstructor builder =
+            new VCardEntryConstructor(null, null, false, vCardType, null);
+        builder.addEntryHandler(this);
+        try {
+            vCardParser.parse(is, builder);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    public void onStart() {
+        for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
+            elem.onParsingStart();
+        }
+    }
+
+    public void onEntryCreated(VCardEntry entry) {
+        mTestCase.assertTrue(mIndex < mContentValuesVerifierElemList.size());
+        mContentValuesVerifierElemList.get(mIndex).onEntryCreated(entry);
+        mIndex++;
+    }
+
+    public void onEnd() {
+        for (ContentValuesVerifierElem elem : mContentValuesVerifierElemList) {
+            elem.onParsingEnd();
+            elem.verifyResolver();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java
new file mode 100644
index 0000000..2edbb36
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ContentValuesVerifierElem.java
@@ -0,0 +1,95 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardConfig;
+import android.pim.vcard.VCardEntry;
+import android.pim.vcard.VCardEntryCommitter;
+import android.pim.vcard.VCardEntryConstructor;
+import android.pim.vcard.VCardEntryHandler;
+import android.pim.vcard.VCardParser;
+import android.pim.vcard.VCardParser_V21;
+import android.pim.vcard.VCardParser_V30;
+import android.pim.vcard.exception.VCardException;
+import android.provider.ContactsContract.Data;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/* package */ class ContentValuesVerifierElem {
+    private final AndroidTestCase mTestCase;
+    private final ImportTestResolver mResolver;
+    private final VCardEntryHandler mHandler;
+
+    public ContentValuesVerifierElem(AndroidTestCase androidTestCase) {
+        mTestCase = androidTestCase;
+        mResolver = new ImportTestResolver(androidTestCase);
+        mHandler = new VCardEntryCommitter(mResolver);
+    }
+
+    public ContentValuesBuilder addExpected(String mimeType) {
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(Data.MIMETYPE, mimeType);
+        mResolver.addExpectedContentValues(contentValues);
+        return new ContentValuesBuilder(contentValues);
+    }
+
+    public void verify(int resId, int vCardType)
+            throws IOException, VCardException {
+        verify(mTestCase.getContext().getResources().openRawResource(resId), vCardType);
+    }
+
+    public void verify(InputStream is, int vCardType) throws IOException, VCardException {
+        final VCardParser vCardParser;
+        if (VCardConfig.isV30(vCardType)) {
+            vCardParser = new VCardParser_V30(true);  // use StrictParsing
+        } else {
+            vCardParser = new VCardParser_V21();
+        }
+        VCardEntryConstructor builder =
+                new VCardEntryConstructor(null, null, false, vCardType, null);
+        builder.addEntryHandler(mHandler);
+        try {
+            vCardParser.parse(is, builder);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        verifyResolver();
+    }
+
+    public void verifyResolver() {
+        mResolver.verify();
+    }
+
+    public void onParsingStart() {
+        mHandler.onStart();
+    }
+
+    public void onEntryCreated(VCardEntry entry) {
+        mHandler.onEntryCreated(entry);
+    }
+
+    public void onParsingEnd() {
+        mHandler.onEnd();
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
new file mode 100644
index 0000000..1b3cdcc
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ExportTestResolver.java
@@ -0,0 +1,213 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Entity;
+import android.content.EntityIterator;
+import android.database.Cursor;
+import android.net.Uri;
+import android.pim.vcard.VCardComposer;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.test.mock.MockContentResolver;
+import android.test.mock.MockCursor;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/* package */ public class ExportTestResolver extends MockContentResolver {
+    ExportTestProvider mProvider;
+    public ExportTestResolver(TestCase testCase) {
+        mProvider = new ExportTestProvider(testCase);
+        addProvider(VCardComposer.VCARD_TEST_AUTHORITY, mProvider);
+        addProvider(RawContacts.CONTENT_URI.getAuthority(), mProvider);
+    }
+
+    public ContactEntry addInputContactEntry() {
+        return mProvider.buildInputEntry();
+    }
+}
+
+/* package */ class MockEntityIterator implements EntityIterator {
+    List<Entity> mEntityList;
+    Iterator<Entity> mIterator;
+
+    public MockEntityIterator(List<ContentValues> contentValuesList) {
+        mEntityList = new ArrayList<Entity>();
+        Entity entity = new Entity(new ContentValues());
+        for (ContentValues contentValues : contentValuesList) {
+                entity.addSubValue(Data.CONTENT_URI, contentValues);
+        }
+        mEntityList.add(entity);
+        mIterator = mEntityList.iterator();
+    }
+
+    public boolean hasNext() {
+        return mIterator.hasNext();
+    }
+
+    public Entity next() {
+        return mIterator.next();
+    }
+
+    public void reset() {
+        mIterator = mEntityList.iterator();
+    }
+
+    public void close() {
+    }
+}
+
+/**
+ * Represents one contact, which should contain multiple ContentValues like
+ * StructuredName, Email, etc.
+ */
+/* package */ class ContactEntry {
+    private final List<ContentValues> mContentValuesList = new ArrayList<ContentValues>();
+
+    public ContentValuesBuilder addContentValues(String mimeType) {
+        ContentValues contentValues = new ContentValues();
+        contentValues.put(Data.MIMETYPE, mimeType);
+        mContentValuesList.add(contentValues);
+        return new ContentValuesBuilder(contentValues);
+    }
+
+    public List<ContentValues> getList() {
+        return mContentValuesList;
+    }
+}
+
+/* package */ class ExportTestProvider extends MockContentProvider {
+    final private TestCase mTestCase;
+    final private ArrayList<ContactEntry> mContactEntryList = new ArrayList<ContactEntry>();
+
+    public ExportTestProvider(TestCase testCase) {
+        mTestCase = testCase;
+    }
+
+    public ContactEntry buildInputEntry() {
+        ContactEntry contactEntry = new ContactEntry();
+        mContactEntryList.add(contactEntry);
+        return contactEntry;
+    }
+
+    /**
+     * <p>
+     * An old method which had existed but was removed from ContentResolver.
+     * </p>
+     * <p>
+     * We still keep using this method since we don't have a propeer way to know
+     * which value in the ContentValue corresponds to the entry in Contacts database.
+     * </p>
+     * <p>
+     * Detail:
+     * There's an easy way to know which index "family name" corresponds to, via
+     * {@link android.provider.ContactsContract}.
+     * FAMILY_NAME equals DATA3, so the corresponding index
+     * for "family name" should be 2 (note that index is 0-origin).
+     * However, we cannot know what the index 2 corresponds to; it may be "family name",
+     * "label" for now, but may be the other some column in the future. We don't have
+     * convenient way to know the original data structure.
+     * </p>
+     */
+    public EntityIterator queryEntities(Uri uri,
+            String selection, String[] selectionArgs, String sortOrder) {
+        mTestCase.assertTrue(uri != null);
+        mTestCase.assertTrue(ContentResolver.SCHEME_CONTENT.equals(uri.getScheme()));
+        final String authority = uri.getAuthority();
+        mTestCase.assertTrue(RawContacts.CONTENT_URI.getAuthority().equals(authority));
+        mTestCase.assertTrue((Data.CONTACT_ID + "=?").equals(selection));
+        mTestCase.assertEquals(1, selectionArgs.length);
+        final int id = Integer.parseInt(selectionArgs[0]);
+        mTestCase.assertTrue(id >= 0 && id < mContactEntryList.size());
+
+        return new MockEntityIterator(mContactEntryList.get(id).getList());
+    }
+
+    @Override
+    public Cursor query(Uri uri,String[] projection,
+            String selection, String[] selectionArgs, String sortOrder) {
+        mTestCase.assertTrue(VCardComposer.CONTACTS_TEST_CONTENT_URI.equals(uri));
+        // In this test, following arguments are not supported.
+        mTestCase.assertNull(selection);
+        mTestCase.assertNull(selectionArgs);
+        mTestCase.assertNull(sortOrder);
+
+        return new MockCursor() {
+            int mCurrentPosition = -1;
+
+            @Override
+            public int getCount() {
+                return mContactEntryList.size();
+            }
+
+            @Override
+            public boolean moveToFirst() {
+                mCurrentPosition = 0;
+                return true;
+            }
+
+            @Override
+            public boolean moveToNext() {
+                if (mCurrentPosition < mContactEntryList.size()) {
+                    mCurrentPosition++;
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+
+            @Override
+            public boolean isBeforeFirst() {
+                return mCurrentPosition < 0;
+            }
+
+            @Override
+            public boolean isAfterLast() {
+                return mCurrentPosition >= mContactEntryList.size();
+            }
+
+            @Override
+            public int getColumnIndex(String columnName) {
+                mTestCase.assertEquals(Contacts._ID, columnName);
+                return 0;
+            }
+
+            @Override
+            public int getInt(int columnIndex) {
+                mTestCase.assertEquals(0, columnIndex);
+                mTestCase.assertTrue(mCurrentPosition >= 0
+                        && mCurrentPosition < mContactEntryList.size());
+                return mCurrentPosition;
+            }
+
+            @Override
+            public String getString(int columnIndex) {
+                return String.valueOf(getInt(columnIndex));
+            }
+
+            @Override
+            public void close() {
+            }
+        };
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
new file mode 100644
index 0000000..019b9e3
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/ImportTestResolver.java
@@ -0,0 +1,299 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.test.mock.MockContentResolver;
+import android.text.TextUtils;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.Map.Entry;
+
+/* package */ class ImportTestResolver extends MockContentResolver {
+    final ImportTestProvider mProvider;
+
+    public ImportTestResolver(TestCase testCase) {
+        mProvider = new ImportTestProvider(testCase);
+    }
+
+    @Override
+    public ContentProviderResult[] applyBatch(String authority,
+            ArrayList<ContentProviderOperation> operations) {
+        equalsString(authority, RawContacts.CONTENT_URI.toString());
+        return mProvider.applyBatch(operations);
+    }
+
+    public void addExpectedContentValues(ContentValues expectedContentValues) {
+        mProvider.addExpectedContentValues(expectedContentValues);
+    }
+
+    public void verify() {
+        mProvider.verify();
+    }
+
+    private static boolean equalsString(String a, String b) {
+        if (a == null || a.length() == 0) {
+            return b == null || b.length() == 0;
+        } else {
+            return a.equals(b);
+        }
+    }
+}
+
+/* package */ class ImportTestProvider extends MockContentProvider {
+    private static final Set<String> sKnownMimeTypeSet =
+        new HashSet<String>(Arrays.asList(StructuredName.CONTENT_ITEM_TYPE,
+                Nickname.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE,
+                Email.CONTENT_ITEM_TYPE, StructuredPostal.CONTENT_ITEM_TYPE,
+                Im.CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE,
+                Event.CONTENT_ITEM_TYPE, Photo.CONTENT_ITEM_TYPE,
+                Note.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE,
+                Relation.CONTENT_ITEM_TYPE, Event.CONTENT_ITEM_TYPE,
+                GroupMembership.CONTENT_ITEM_TYPE));
+
+    final Map<String, Collection<ContentValues>> mMimeTypeToExpectedContentValues;
+
+    private final TestCase mTestCase;
+
+    public ImportTestProvider(TestCase testCase) {
+        mTestCase = testCase;
+        mMimeTypeToExpectedContentValues =
+            new HashMap<String, Collection<ContentValues>>();
+        for (String acceptanbleMimeType : sKnownMimeTypeSet) {
+            // Do not use HashSet since the current implementation changes the content of
+            // ContentValues after the insertion, which make the result of hashCode()
+            // changes...
+            mMimeTypeToExpectedContentValues.put(
+                    acceptanbleMimeType, new ArrayList<ContentValues>());
+        }
+    }
+
+    public void addExpectedContentValues(ContentValues expectedContentValues) {
+        final String mimeType = expectedContentValues.getAsString(Data.MIMETYPE);
+        if (!sKnownMimeTypeSet.contains(mimeType)) {
+            mTestCase.fail(String.format(
+                    "Unknow MimeType %s in the test code. Test code should be broken.",
+                    mimeType));
+        }
+
+        final Collection<ContentValues> contentValuesCollection =
+                mMimeTypeToExpectedContentValues.get(mimeType);
+        contentValuesCollection.add(expectedContentValues);
+    }
+
+    @Override
+    public ContentProviderResult[] applyBatch(
+            ArrayList<ContentProviderOperation> operations) {
+        if (operations == null) {
+            mTestCase.fail("There is no operation.");
+        }
+
+        final int size = operations.size();
+        ContentProviderResult[] fakeResultArray = new ContentProviderResult[size];
+        for (int i = 0; i < size; i++) {
+            Uri uri = Uri.withAppendedPath(RawContacts.CONTENT_URI, String.valueOf(i));
+            fakeResultArray[i] = new ContentProviderResult(uri);
+        }
+
+        for (int i = 0; i < size; i++) {
+            ContentProviderOperation operation = operations.get(i);
+            ContentValues contentValues = operation.resolveValueBackReferences(
+                    fakeResultArray, i);
+        }
+        for (int i = 0; i < size; i++) {
+            ContentProviderOperation operation = operations.get(i);
+            ContentValues actualContentValues = operation.resolveValueBackReferences(
+                    fakeResultArray, i);
+            final Uri uri = operation.getUri();
+            if (uri.equals(RawContacts.CONTENT_URI)) {
+                mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_NAME));
+                mTestCase.assertNull(actualContentValues.get(RawContacts.ACCOUNT_TYPE));
+            } else if (uri.equals(Data.CONTENT_URI)) {
+                final String mimeType = actualContentValues.getAsString(Data.MIMETYPE);
+                if (!sKnownMimeTypeSet.contains(mimeType)) {
+                    mTestCase.fail(String.format(
+                            "Unknown MimeType %s. Probably added after developing this test",
+                            mimeType));
+                }
+                // Remove data meaningless in this unit tests.
+                // Specifically, Data.DATA1 - DATA7 are set to null or empty String
+                // regardless of the input, but it may change depending on how
+                // resolver-related code handles it.
+                // Here, we ignore these implementation-dependent specs and
+                // just check whether vCard importer correctly inserts rellevent data.
+                Set<String> keyToBeRemoved = new HashSet<String>();
+                for (Entry<String, Object> entry : actualContentValues.valueSet()) {
+                    Object value = entry.getValue();
+                    if (value == null || TextUtils.isEmpty(value.toString())) {
+                        keyToBeRemoved.add(entry.getKey());
+                    }
+                }
+                for (String key: keyToBeRemoved) {
+                    actualContentValues.remove(key);
+                }
+                /* for testing
+                Log.d("@@@",
+                        String.format("MimeType: %s, data: %s",
+                                mimeType, actualContentValues.toString())); */
+                // Remove RAW_CONTACT_ID entry just for safety, since we do not care
+                // how resolver-related code handles the entry in this unit test,
+                if (actualContentValues.containsKey(Data.RAW_CONTACT_ID)) {
+                    actualContentValues.remove(Data.RAW_CONTACT_ID);
+                }
+                final Collection<ContentValues> contentValuesCollection =
+                    mMimeTypeToExpectedContentValues.get(mimeType);
+                if (contentValuesCollection.isEmpty()) {
+                    mTestCase.fail("ContentValues for MimeType " + mimeType
+                            + " is not expected at all (" + actualContentValues + ")");
+                }
+                boolean checked = false;
+                for (ContentValues expectedContentValues : contentValuesCollection) {
+                    /*for testing
+                    Log.d("@@@", "expected: "
+                            + convertToEasilyReadableString(expectedContentValues));
+                    Log.d("@@@", "actual  : "
+                            + convertToEasilyReadableString(actualContentValues));*/
+                    if (equalsForContentValues(expectedContentValues,
+                            actualContentValues)) {
+                        mTestCase.assertTrue(contentValuesCollection.remove(expectedContentValues));
+                        checked = true;
+                        break;
+                    }
+                }
+                if (!checked) {
+                    final StringBuilder builder = new StringBuilder();
+                    builder.append("Unexpected: ");
+                    builder.append(convertToEasilyReadableString(actualContentValues));
+                    builder.append("\nExpected: ");
+                    for (ContentValues expectedContentValues : contentValuesCollection) {
+                        builder.append(convertToEasilyReadableString(expectedContentValues));
+                    }
+                    mTestCase.fail(builder.toString());
+                }
+            } else {
+                mTestCase.fail("Unexpected Uri has come: " + uri);
+            }
+        }  // for (int i = 0; i < size; i++) {
+        return null;
+    }
+
+    public void verify() {
+        StringBuilder builder = new StringBuilder();
+        for (Collection<ContentValues> contentValuesCollection :
+                mMimeTypeToExpectedContentValues.values()) {
+            for (ContentValues expectedContentValues: contentValuesCollection) {
+                builder.append(convertToEasilyReadableString(expectedContentValues));
+                builder.append("\n");
+            }
+        }
+        if (builder.length() > 0) {
+            final String failMsg =
+                "There is(are) remaining expected ContentValues instance(s): \n"
+                    + builder.toString();
+            mTestCase.fail(failMsg);
+        }
+    }
+
+    /**
+     * Utility method to print ContentValues whose content is printed with sorted keys.
+     */
+    private String convertToEasilyReadableString(ContentValues contentValues) {
+        if (contentValues == null) {
+            return "null";
+        }
+        String mimeTypeValue = "";
+        SortedMap<String, String> sortedMap = new TreeMap<String, String>();
+        for (Entry<String, Object> entry : contentValues.valueSet()) {
+            final String key = entry.getKey();
+            final Object value = entry.getValue();
+            final String valueString = (value != null ? value.toString() : null);
+            if (Data.MIMETYPE.equals(key)) {
+                mimeTypeValue = valueString;
+            } else {
+                mTestCase.assertNotNull(key);
+                sortedMap.put(key, valueString);
+            }
+        }
+        StringBuilder builder = new StringBuilder();
+        builder.append(Data.MIMETYPE);
+        builder.append('=');
+        builder.append(mimeTypeValue);
+        for (Entry<String, String> entry : sortedMap.entrySet()) {
+            final String key = entry.getKey();
+            final String value = entry.getValue();
+            builder.append(' ');
+            builder.append(key);
+            builder.append("=\"");
+            builder.append(value);
+            builder.append('"');
+        }
+        return builder.toString();
+    }
+
+    private static boolean equalsForContentValues(
+            ContentValues expected, ContentValues actual) {
+        if (expected == actual) {
+            return true;
+        } else if (expected == null || actual == null || expected.size() != actual.size()) {
+            return false;
+        }
+
+        for (Entry<String, Object> entry : expected.valueSet()) {
+            final String key = entry.getKey();
+            final Object value = entry.getValue();
+            if (!actual.containsKey(key)) {
+                return false;
+            }
+            if (value instanceof byte[]) {
+                Object actualValue = actual.get(key);
+                if (!Arrays.equals((byte[])value, (byte[])actualValue)) {
+                    return false;
+                }
+            } else if (!value.equals(actual.get(key))) {
+                    return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifier.java b/core/tests/coretests/src/android/pim/vcard/LineVerifier.java
new file mode 100644
index 0000000..cef15fd7
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/LineVerifier.java
@@ -0,0 +1,65 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.Context;
+import android.pim.vcard.VCardComposer;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+
+class LineVerifier implements VCardComposer.OneEntryHandler {
+    private final TestCase mTestCase;
+    private final ArrayList<LineVerifierElem> mLineVerifierElemList;
+    private int mVCardType;
+    private int index;
+
+    public LineVerifier(TestCase testCase, int vcardType) {
+        mTestCase = testCase;
+        mLineVerifierElemList = new ArrayList<LineVerifierElem>();
+        mVCardType = vcardType;
+    }
+
+    public LineVerifierElem addLineVerifierElem() {
+        LineVerifierElem lineVerifier = new LineVerifierElem(mTestCase, mVCardType);
+        mLineVerifierElemList.add(lineVerifier);
+        return lineVerifier;
+    }
+
+    public void verify(String vcard) {
+        if (index >= mLineVerifierElemList.size()) {
+            mTestCase.fail("Insufficient number of LineVerifier (" + index + ")");
+        }
+
+        LineVerifierElem lineVerifier = mLineVerifierElemList.get(index);
+        lineVerifier.verify(vcard);
+
+        index++;
+    }
+
+    public boolean onEntryCreated(String vcard) {
+        verify(vcard);
+        return true;
+    }
+
+    public boolean onInit(Context context) {
+        return true;
+    }
+
+    public void onTerminate() {
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java b/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java
new file mode 100644
index 0000000..b23b29b
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/LineVerifierElem.java
@@ -0,0 +1,105 @@
+/*
+ * 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.pim.vcard;
+
+import android.pim.vcard.VCardConfig;
+import android.text.TextUtils;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class LineVerifierElem {
+    private final TestCase mTestCase;
+    private final List<String> mExpectedLineList = new ArrayList<String>();
+    private final boolean mIsV30;
+
+    public LineVerifierElem(TestCase testCase, int vcardType) {
+        mTestCase = testCase;
+        mIsV30 = VCardConfig.isV30(vcardType);
+    }
+
+    public LineVerifierElem addExpected(final String line) {
+        if (!TextUtils.isEmpty(line)) {
+            mExpectedLineList.add(line);
+        }
+        return this;
+    }
+
+    public void verify(final String vcard) {
+        final String[] lineArray = vcard.split("\\r?\\n");
+        final int length = lineArray.length;
+        boolean beginExists = false;
+        boolean endExists = false;
+        boolean versionExists = false;
+
+        for (int i = 0; i < length; i++) {
+            final String line = lineArray[i];
+            if (TextUtils.isEmpty(line)) {
+                continue;
+            }
+
+            if ("BEGIN:VCARD".equalsIgnoreCase(line)) {
+                if (beginExists) {
+                    mTestCase.fail("Multiple \"BEGIN:VCARD\" line found");
+                } else {
+                    beginExists = true;
+                    continue;
+                }
+            } else if ("END:VCARD".equalsIgnoreCase(line)) {
+                if (endExists) {
+                    mTestCase.fail("Multiple \"END:VCARD\" line found");
+                } else {
+                    endExists = true;
+                    continue;
+                }
+            } else if ((mIsV30 ? "VERSION:3.0" : "VERSION:2.1").equalsIgnoreCase(line)) {
+                if (versionExists) {
+                    mTestCase.fail("Multiple VERSION line + found");
+                } else {
+                    versionExists = true;
+                    continue;
+                }
+            }
+
+            if (!beginExists) {
+                mTestCase.fail("Property other than BEGIN came before BEGIN property: "
+                        + line);
+            } else if (endExists) {
+                mTestCase.fail("Property other than END came after END property: "
+                        + line);
+            }
+
+            final int index = mExpectedLineList.indexOf(line);
+            if (index >= 0) {
+                mExpectedLineList.remove(index);
+            } else {
+                mTestCase.fail("Unexpected line: " + line);
+            }
+        }
+
+        if (!mExpectedLineList.isEmpty()) {
+            StringBuffer buffer = new StringBuffer();
+            for (String expectedLine : mExpectedLineList) {
+                buffer.append(expectedLine);
+                buffer.append("\n");
+            }
+
+            mTestCase.fail("Expected line(s) not found:" + buffer.toString());
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/PropertyNode.java b/core/tests/coretests/src/android/pim/vcard/PropertyNode.java
new file mode 100644
index 0000000..2c1f6d2
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/PropertyNode.java
@@ -0,0 +1,198 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardEntry;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Previously used in main vCard handling code but now exists only for testing.
+ *
+ * Especially useful for testing parser code (VCardParser), since all properties can be
+ * checked via this class unlike {@link VCardEntry}, which only emits the result of
+ * interpretation of the content of each vCard. We cannot know whether vCard parser or
+ * ContactStruct is wrong withouth this class.
+ */
+public class PropertyNode {
+    public String propName;
+    public String propValue;
+    public List<String> propValue_vector;
+
+    /** Store value as byte[],after decode.
+     * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc.
+     */
+    public byte[] propValue_bytes;
+
+    /** param store: key=paramType, value=paramValue
+     * Note that currently PropertyNode class does not support multiple param-values
+     * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
+     * one String value like "A,B", not ["A", "B"]...
+     * TODO: fix this. 
+     */
+    public ContentValues paramMap;
+
+    /** Only for TYPE=??? param store. */
+    public Set<String> paramMap_TYPE;
+
+    /** Store group values. Used only in VCard. */
+    public Set<String> propGroupSet;
+    
+    public PropertyNode() {
+        propName = "";
+        propValue = "";
+        propValue_vector = new ArrayList<String>();
+        paramMap = new ContentValues();
+        paramMap_TYPE = new HashSet<String>();
+        propGroupSet = new HashSet<String>();
+    }
+    
+    public PropertyNode(
+            String propName, String propValue, List<String> propValue_vector,
+            byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE,
+            Set<String> propGroupSet) {
+        if (propName != null) {
+            this.propName = propName;
+        } else {
+            this.propName = "";
+        }
+        if (propValue != null) {
+            this.propValue = propValue;
+        } else {
+            this.propValue = "";
+        }
+        if (propValue_vector != null) {
+            this.propValue_vector = propValue_vector;
+        } else {
+            this.propValue_vector = new ArrayList<String>();
+        }
+        this.propValue_bytes = propValue_bytes;
+        if (paramMap != null) {
+            this.paramMap = paramMap;
+        } else {
+            this.paramMap = new ContentValues();
+        }
+        if (paramMap_TYPE != null) {
+            this.paramMap_TYPE = paramMap_TYPE;
+        } else {
+            this.paramMap_TYPE = new HashSet<String>();
+        }
+        if (propGroupSet != null) {
+            this.propGroupSet = propGroupSet;
+        } else {
+            this.propGroupSet = new HashSet<String>();
+        }
+    }
+    
+    @Override
+    public int hashCode() {
+        // vCard may contain more than one same line in one entry, while HashSet or any other
+        // library which utilize hashCode() does not honor that, so intentionally throw an
+        // Exception.
+        throw new UnsupportedOperationException(
+                "PropertyNode does not provide hashCode() implementation intentionally.");
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof PropertyNode)) {
+            return false;
+        }
+        
+        PropertyNode node = (PropertyNode)obj;
+        
+        if (propName == null || !propName.equals(node.propName)) {
+            return false;
+        } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
+            return false;
+        } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
+            return false;
+        } else if (!propGroupSet.equals(node.propGroupSet)) {
+            return false;
+        }
+
+        if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) {
+            return true;
+        } else {
+            if (!propValue.equals(node.propValue)) {
+                return false;
+            }
+
+            // The value in propValue_vector is not decoded even if it should be
+            // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector
+            // is 1, the encoded value is stored in propValue, so we do not have to
+            // check it.
+            return (propValue_vector.equals(node.propValue_vector) ||
+                    propValue_vector.size() == 1 ||
+                    node.propValue_vector.size() == 1);
+        }
+    }
+    
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("propName: ");
+        builder.append(propName);
+        builder.append(", paramMap: ");
+        builder.append(paramMap.toString());
+        builder.append(", paramMap_TYPE: [");
+        boolean first = true;
+        for (String elem : paramMap_TYPE) {
+            if (first) {
+                first = false;
+            } else {
+                builder.append(", ");
+            }
+            builder.append('"');
+            builder.append(elem);
+            builder.append('"');
+        }
+        builder.append("]");
+        if (!propGroupSet.isEmpty()) {
+            builder.append(", propGroupSet: [");
+            first = true;
+            for (String elem : propGroupSet) {
+                if (first) {
+                    first = false;
+                } else {
+                    builder.append(", ");
+                }
+                builder.append('"');
+                builder.append(elem);
+                builder.append('"');
+            }
+            builder.append("]");
+        }
+        if (propValue_vector != null && propValue_vector.size() > 1) {
+            builder.append(", propValue_vector size: ");
+            builder.append(propValue_vector.size());
+        }
+        if (propValue_bytes != null) {
+            builder.append(", propValue_bytes size: ");
+            builder.append(propValue_bytes.length);
+        }
+        builder.append(", propValue: \"");
+        builder.append(propValue);
+        builder.append("\"");
+        return builder.toString();
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java b/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java
new file mode 100644
index 0000000..cfdd074
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/PropertyNodesVerifier.java
@@ -0,0 +1,386 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardConfig;
+import android.pim.vcard.VCardParser;
+import android.pim.vcard.VCardParser_V21;
+import android.pim.vcard.VCardParser_V30;
+import android.pim.vcard.exception.VCardException;
+import android.test.AndroidTestCase;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+/* package */ class PropertyNodesVerifier extends VNodeBuilder {
+    private final List<PropertyNodesVerifierElem> mPropertyNodesVerifierElemList;
+    private final AndroidTestCase mAndroidTestCase;
+    private int mIndex;
+
+    public PropertyNodesVerifier(AndroidTestCase testCase) {
+        mPropertyNodesVerifierElemList = new ArrayList<PropertyNodesVerifierElem>();
+        mAndroidTestCase = testCase;
+    }
+
+    public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
+        PropertyNodesVerifierElem elem = new PropertyNodesVerifierElem(mAndroidTestCase);
+        mPropertyNodesVerifierElemList.add(elem);
+        return elem;
+    }
+
+    public void verify(int resId, int vCardType)
+            throws IOException, VCardException {
+        verify(mAndroidTestCase.getContext().getResources().openRawResource(resId), vCardType);
+    }
+
+    public void verify(int resId, int vCardType, final VCardParser vCardParser)
+            throws IOException, VCardException {
+        verify(mAndroidTestCase.getContext().getResources().openRawResource(resId),
+                vCardType, vCardParser);
+    }
+
+    public void verify(InputStream is, int vCardType) throws IOException, VCardException {
+        final VCardParser vCardParser;
+        if (VCardConfig.isV30(vCardType)) {
+            vCardParser = new VCardParser_V30(true);  // Use StrictParsing.
+        } else {
+            vCardParser = new VCardParser_V21();
+        }
+        verify(is, vCardType, vCardParser);
+    }
+
+    public void verify(InputStream is, int vCardType, final VCardParser vCardParser)
+            throws IOException, VCardException {
+        try {
+            vCardParser.parse(is, this);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    @Override
+    public void endEntry() {
+        super.endEntry();
+        mAndroidTestCase.assertTrue(mIndex < mPropertyNodesVerifierElemList.size());
+        mAndroidTestCase.assertTrue(mIndex < vNodeList.size());
+        mPropertyNodesVerifierElemList.get(mIndex).verify(vNodeList.get(mIndex));
+        mIndex++;
+    }
+}
+
+/**
+ * Utility class which verifies input VNode.
+ *
+ * This class first checks whether each propertyNode in the VNode is in the
+ * "ordered expected property list".
+ * If the node does not exist in the "ordered list", the class refers to
+ * "unorderd expected property set" and checks the node is expected somewhere.
+ */
+/* package */ class PropertyNodesVerifierElem {
+    public static class TypeSet extends HashSet<String> {
+        public TypeSet(String ... array) {
+            super(Arrays.asList(array));
+        }
+    }
+
+    public static class GroupSet extends HashSet<String> {
+        public GroupSet(String ... array) {
+            super(Arrays.asList(array));
+        }
+    }
+
+    private final HashMap<String, List<PropertyNode>> mOrderedNodeMap;
+    // Intentionally use ArrayList instead of Set, assuming there may be more than one
+    // exactly same objects.
+    private final ArrayList<PropertyNode> mUnorderedNodeList;
+    private final TestCase mTestCase;
+
+    public PropertyNodesVerifierElem(TestCase testCase) {
+        mOrderedNodeMap = new HashMap<String, List<PropertyNode>>();
+        mUnorderedNodeList = new ArrayList<PropertyNode>();
+        mTestCase = testCase;
+    }
+
+    // WithOrder
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue) {
+        return addExpectedNodeWithOrder(propName, propValue, null, null, null, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+            String propName, String propValue, ContentValues contentValues) {
+        return addExpectedNodeWithOrder(propName, propValue, null,
+                null, contentValues, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+            String propName, List<String> propValueList, ContentValues contentValues) {
+        return addExpectedNodeWithOrder(propName, null, propValueList,
+                null, contentValues, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+            String propName, String propValue, List<String> propValueList) {
+        return addExpectedNodeWithOrder(propName, propValue, propValueList, null,
+                null, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(
+            String propName, List<String> propValueList) {
+        final String propValue = concatinateListWithSemiColon(propValueList);
+        return addExpectedNodeWithOrder(propName, propValue.toString(), propValueList,
+                null, null, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
+            TypeSet paramMap_TYPE) {
+        return addExpectedNodeWithOrder(propName, propValue, null,
+                null, null, paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName,
+            List<String> propValueList, TypeSet paramMap_TYPE) {
+        return addExpectedNodeWithOrder(propName, null, propValueList, null, null,
+                paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
+            ContentValues paramMap, TypeSet paramMap_TYPE) {
+        return addExpectedNodeWithOrder(propName, propValue, null, null,
+                paramMap, paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
+            List<String> propValueList, TypeSet paramMap_TYPE) {
+        return addExpectedNodeWithOrder(propName, propValue, propValueList, null, null,
+                paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNodeWithOrder(String propName, String propValue,
+            List<String> propValueList, byte[] propValue_bytes,
+            ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
+        if (propValue == null && propValueList != null) {
+            propValue = concatinateListWithSemiColon(propValueList);
+        }
+        PropertyNode propertyNode = new PropertyNode(propName,
+                propValue, propValueList, propValue_bytes,
+                paramMap, paramMap_TYPE, propGroupSet);
+        List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
+        if (expectedNodeList == null) {
+            expectedNodeList = new ArrayList<PropertyNode>();
+            mOrderedNodeMap.put(propName, expectedNodeList);
+        }
+        expectedNodeList.add(propertyNode);
+        return this;
+    }
+
+    // WithoutOrder
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue) {
+        return addExpectedNode(propName, propValue, null, null, null, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+            ContentValues contentValues) {
+        return addExpectedNode(propName, propValue, null, null, contentValues, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName,
+            List<String> propValueList, ContentValues contentValues) {
+        return addExpectedNode(propName, null,
+                propValueList, null, contentValues, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+            List<String> propValueList) {
+        return addExpectedNode(propName, propValue, propValueList, null, null, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName,
+            List<String> propValueList) {
+        return addExpectedNode(propName, null, propValueList,
+                null, null, null, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+            TypeSet paramMap_TYPE) {
+        return addExpectedNode(propName, propValue, null, null, null, paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName,
+            List<String> propValueList, TypeSet paramMap_TYPE) {
+        final String propValue = concatinateListWithSemiColon(propValueList);
+        return addExpectedNode(propName, propValue, propValueList, null, null,
+                paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+            List<String> propValueList, TypeSet paramMap_TYPE) {
+        return addExpectedNode(propName, propValue, propValueList, null, null,
+                paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+            ContentValues paramMap, TypeSet paramMap_TYPE) {
+        return addExpectedNode(propName, propValue, null, null,
+                paramMap, paramMap_TYPE, null);
+    }
+
+    public PropertyNodesVerifierElem addExpectedNode(String propName, String propValue,
+            List<String> propValueList, byte[] propValue_bytes,
+            ContentValues paramMap, TypeSet paramMap_TYPE, GroupSet propGroupSet) {
+        if (propValue == null && propValueList != null) {
+            propValue = concatinateListWithSemiColon(propValueList);
+        }
+        mUnorderedNodeList.add(new PropertyNode(propName, propValue,
+                propValueList, propValue_bytes, paramMap, paramMap_TYPE, propGroupSet));
+        return this;
+    }
+
+    public void verify(VNode vnode) {
+        for (PropertyNode actualNode : vnode.propList) {
+            verifyNode(actualNode.propName, actualNode);
+        }
+        if (!mOrderedNodeMap.isEmpty() || !mUnorderedNodeList.isEmpty()) {
+            List<String> expectedProps = new ArrayList<String>();
+            for (List<PropertyNode> nodes : mOrderedNodeMap.values()) {
+                for (PropertyNode node : nodes) {
+                    if (!expectedProps.contains(node.propName)) {
+                        expectedProps.add(node.propName);
+                    }
+                }
+            }
+            for (PropertyNode node : mUnorderedNodeList) {
+                if (!expectedProps.contains(node.propName)) {
+                    expectedProps.add(node.propName);
+                }
+            }
+            mTestCase.fail("Expected property " + Arrays.toString(expectedProps.toArray())
+                    + " was not found.");
+        }
+    }
+
+    private void verifyNode(final String propName, final PropertyNode actualNode) {
+        List<PropertyNode> expectedNodeList = mOrderedNodeMap.get(propName);
+        final int size = (expectedNodeList != null ? expectedNodeList.size() : 0);
+        if (size > 0) {
+            for (int i = 0; i < size; i++) {
+                PropertyNode expectedNode = expectedNodeList.get(i);
+                List<PropertyNode> expectedButDifferentValueList = new ArrayList<PropertyNode>();
+                if (expectedNode.propName.equals(propName)) {
+                    if (expectedNode.equals(actualNode)) {
+                        expectedNodeList.remove(i);
+                        if (expectedNodeList.size() == 0) {
+                            mOrderedNodeMap.remove(propName);
+                        }
+                        return;
+                    } else {
+                        expectedButDifferentValueList.add(expectedNode);
+                    }
+                }
+
+                // "actualNode" is not in ordered expected list.
+                // Try looking over unordered expected list.
+                if (tryFoundExpectedNodeFromUnorderedList(actualNode,
+                        expectedButDifferentValueList)) {
+                    return;
+                }
+
+                if (!expectedButDifferentValueList.isEmpty()) {
+                    // Same propName exists but with different value(s).
+                    failWithExpectedNodeList(propName, actualNode,
+                            expectedButDifferentValueList);
+                } else {
+                    // There's no expected node with same propName.
+                    mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
+                }
+            }
+        } else {
+            List<PropertyNode> expectedButDifferentValueList =
+                new ArrayList<PropertyNode>();
+            if (tryFoundExpectedNodeFromUnorderedList(actualNode, expectedButDifferentValueList)) {
+                return;
+            } else {
+                if (!expectedButDifferentValueList.isEmpty()) {
+                    // Same propName exists but with different value(s).
+                    failWithExpectedNodeList(propName, actualNode,
+                            expectedButDifferentValueList);
+                } else {
+                    // There's no expected node with same propName.
+                    mTestCase.fail("Unexpected property \"" + propName + "\" exists.");
+                }
+            }
+        }
+    }
+
+    private String concatinateListWithSemiColon(List<String> array) {
+        StringBuffer buffer = new StringBuffer();
+        boolean first = true;
+        for (String propValueElem : array) {
+            if (first) {
+                first = false;
+            } else {
+                buffer.append(';');
+            }
+            buffer.append(propValueElem);
+        }
+
+        return buffer.toString();
+    }
+
+    private boolean tryFoundExpectedNodeFromUnorderedList(PropertyNode actualNode,
+            List<PropertyNode> expectedButDifferentValueList) {
+        final String propName = actualNode.propName;
+        int unorderedListSize = mUnorderedNodeList.size();
+        for (int i = 0; i < unorderedListSize; i++) {
+            PropertyNode unorderedExpectedNode = mUnorderedNodeList.get(i);
+            if (unorderedExpectedNode.propName.equals(propName)) {
+                if (unorderedExpectedNode.equals(actualNode)) {
+                    mUnorderedNodeList.remove(i);
+                    return true;
+                }
+                expectedButDifferentValueList.add(unorderedExpectedNode);
+            }
+        }
+        return false;
+    }
+
+    private void failWithExpectedNodeList(String propName, PropertyNode actualNode,
+            List<PropertyNode> expectedNodeList) {
+        StringBuilder builder = new StringBuilder();
+        for (PropertyNode expectedNode : expectedNodeList) {
+            builder.append("expected: ");
+            builder.append(expectedNode.toString());
+            builder.append("\n");
+        }
+        mTestCase.fail("Property \"" + propName + "\" has wrong value.\n"
+                + builder.toString()
+                + "  actual: " + actualNode.toString());
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
new file mode 100644
index 0000000..004a197
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardExporterTests.java
@@ -0,0 +1,959 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardConfig;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Im;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.Relation;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+
+import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
+
+import java.util.Arrays;
+
+/**
+ * Tests for the code related to vCard exporter, inculding vCard composer.
+ * This test class depends on vCard importer code, so if tests for vCard importer fail,
+ * the result of this class will not be reliable.
+ */
+public class VCardExporterTests extends VCardTestsBase {
+    private static final byte[] sPhotoByteArray =
+        VCardImporterTests.sPhotoByteArrayForComplicatedCase;
+
+    public void testSimpleV21() {
+        mVerifier.initForExportTest(V21);
+        mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "Ando")
+                .put(StructuredName.GIVEN_NAME, "Roid");
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("FN", "Roid Ando")
+                .addExpectedNode("N", "Ando;Roid;;;",
+                        Arrays.asList("Ando", "Roid", "", "", ""));
+    }
+
+    private void testStructuredNameBasic(int vcardType) {
+        final boolean isV30 = VCardConfig.isV30(vcardType);
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
+                .put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
+                .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
+                .put(StructuredName.PREFIX, "AppropriatePrefix")
+                .put(StructuredName.SUFFIX, "AppropriateSuffix")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle");
+
+        PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("N",
+                        "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+                        + "AppropriatePrefix;AppropriateSuffix",
+                        Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+                                "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+                .addExpectedNodeWithOrder("FN",
+                        "AppropriatePrefix AppropriateGivenName "
+                        + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+                .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+                .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+                .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+        if (isV30) {
+            elem.addExpectedNode("SORT-STRING",
+                    "AppropriatePhoneticGiven AppropriatePhoneticMiddle "
+                    + "AppropriatePhoneticFamily");
+        }
+    }
+
+    public void testStructuredNameBasicV21() {
+        testStructuredNameBasic(V21);
+    }
+
+    public void testStructuredNameBasicV30() {
+        testStructuredNameBasic(V30);
+    }
+
+    /**
+     * Test that only "primary" StructuredName is emitted, so that our vCard file
+     * will not confuse the external importer, assuming there may be some importer
+     * which presume that there's only one property toward each of  "N", "FN", etc.
+     * Note that more than one "N", "FN", etc. properties are acceptable in vCard spec.
+     */
+    private void testStructuredNameUsePrimaryCommon(int vcardType) {
+        final boolean isV30 = (vcardType == V30);
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
+                .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
+                .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
+                .put(StructuredName.PREFIX, "DoNotEmitPrefix1")
+                .put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
+
+        // With "IS_PRIMARY=1". This is what we should use.
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
+                .put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
+                .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
+                .put(StructuredName.PREFIX, "AppropriatePrefix")
+                .put(StructuredName.SUFFIX, "AppropriateSuffix")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
+                .put(StructuredName.IS_PRIMARY, 1);
+
+        // With "IS_PRIMARY=1", but we should ignore this time, since this is second, not first.
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
+                .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
+                .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
+                .put(StructuredName.PREFIX, "DoNotEmitPrefix2")
+                .put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2")
+                .put(StructuredName.IS_PRIMARY, 1);
+
+        PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("N",
+                        "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+                        + "AppropriatePrefix;AppropriateSuffix",
+                        Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+                                "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+                .addExpectedNodeWithOrder("FN",
+                        "AppropriatePrefix AppropriateGivenName "
+                        + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+                .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+                .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+                .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+        if (isV30) {
+            elem.addExpectedNode("SORT-STRING",
+                    "AppropriatePhoneticGiven AppropriatePhoneticMiddle "
+                    + "AppropriatePhoneticFamily");
+        }
+    }
+
+    public void testStructuredNameUsePrimaryV21() {
+        testStructuredNameUsePrimaryCommon(V21);
+    }
+
+    public void testStructuredNameUsePrimaryV30() {
+        testStructuredNameUsePrimaryCommon(V30);
+    }
+
+    /**
+     * Tests that only "super primary" StructuredName is emitted.
+     * See also the comment in {@link #testStructuredNameUsePrimaryCommon(int)}.
+     */
+    private void testStructuredNameUseSuperPrimaryCommon(int vcardType) {
+        final boolean isV30 = (vcardType == V30);
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName1")
+                .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName1")
+                .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName1")
+                .put(StructuredName.PREFIX, "DoNotEmitPrefix1")
+                .put(StructuredName.SUFFIX, "DoNotEmitSuffix1")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily1")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven1")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle1");
+
+        // With "IS_PRIMARY=1", but we should ignore this time.
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName2")
+                .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName2")
+                .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName2")
+                .put(StructuredName.PREFIX, "DoNotEmitPrefix2")
+                .put(StructuredName.SUFFIX, "DoNotEmitSuffix2")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily2")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven2")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle2")
+                .put(StructuredName.IS_PRIMARY, 1);
+
+        // With "IS_SUPER_PRIMARY=1". This is what we should use.
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "AppropriateFamilyName")
+                .put(StructuredName.GIVEN_NAME, "AppropriateGivenName")
+                .put(StructuredName.MIDDLE_NAME, "AppropriateMiddleName")
+                .put(StructuredName.PREFIX, "AppropriatePrefix")
+                .put(StructuredName.SUFFIX, "AppropriateSuffix")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "AppropriatePhoneticFamily")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "AppropriatePhoneticGiven")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "AppropriatePhoneticMiddle")
+                .put(StructuredName.IS_SUPER_PRIMARY, 1);
+
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "DoNotEmitFamilyName3")
+                .put(StructuredName.GIVEN_NAME, "DoNotEmitGivenName3")
+                .put(StructuredName.MIDDLE_NAME, "DoNotEmitMiddleName3")
+                .put(StructuredName.PREFIX, "DoNotEmitPrefix3")
+                .put(StructuredName.SUFFIX, "DoNotEmitSuffix3")
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "DoNotEmitPhoneticFamily3")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "DoNotEmitPhoneticGiven3")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "DoNotEmitPhoneticMiddle3")
+                .put(StructuredName.IS_PRIMARY, 1);
+
+        PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("N",
+                        "AppropriateFamilyName;AppropriateGivenName;AppropriateMiddleName;"
+                        + "AppropriatePrefix;AppropriateSuffix",
+                        Arrays.asList("AppropriateFamilyName", "AppropriateGivenName",
+                                "AppropriateMiddleName", "AppropriatePrefix", "AppropriateSuffix"))
+                .addExpectedNodeWithOrder("FN",
+                        "AppropriatePrefix AppropriateGivenName "
+                        + "AppropriateMiddleName AppropriateFamilyName AppropriateSuffix")
+                .addExpectedNode("X-PHONETIC-FIRST-NAME", "AppropriatePhoneticGiven")
+                .addExpectedNode("X-PHONETIC-MIDDLE-NAME", "AppropriatePhoneticMiddle")
+                .addExpectedNode("X-PHONETIC-LAST-NAME", "AppropriatePhoneticFamily");
+
+        if (isV30) {
+            elem.addExpectedNode("SORT-STRING",
+                    "AppropriatePhoneticGiven AppropriatePhoneticMiddle"
+                    + " AppropriatePhoneticFamily");
+        }
+    }
+
+    public void testStructuredNameUseSuperPrimaryV21() {
+        testStructuredNameUseSuperPrimaryCommon(V21);
+    }
+
+    public void testStructuredNameUseSuperPrimaryV30() {
+        testStructuredNameUseSuperPrimaryCommon(V30);
+    }
+
+    public void testNickNameV30() {
+        mVerifier.initForExportTest(V30);
+        mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
+                .put(Nickname.NAME, "Nicky");
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+            .addExpectedNodeWithOrder("NICKNAME", "Nicky");
+    }
+
+    private void testPhoneBasicCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "1")
+                .put(Phone.TYPE, Phone.TYPE_HOME);
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "1", new TypeSet("HOME"));
+    }
+
+    public void testPhoneBasicV21() {
+        testPhoneBasicCommon(V21);
+    }
+
+    public void testPhoneBasicV30() {
+        testPhoneBasicCommon(V30);
+    }
+
+    /**
+     * Tests that vCard composer emits corresponding type param which we expect.
+     */
+    private void testPhoneVariousTypeSupport(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "10")
+                .put(Phone.TYPE, Phone.TYPE_HOME);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "20")
+                .put(Phone.TYPE, Phone.TYPE_WORK);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "30")
+                .put(Phone.TYPE, Phone.TYPE_FAX_HOME);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "40")
+                .put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "50")
+                .put(Phone.TYPE, Phone.TYPE_MOBILE);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "60")
+                .put(Phone.TYPE, Phone.TYPE_PAGER);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "70")
+                .put(Phone.TYPE, Phone.TYPE_OTHER);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "80")
+                .put(Phone.TYPE, Phone.TYPE_CAR);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "90")
+                .put(Phone.TYPE, Phone.TYPE_COMPANY_MAIN);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "100")
+                .put(Phone.TYPE, Phone.TYPE_ISDN);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "110")
+                .put(Phone.TYPE, Phone.TYPE_MAIN);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "120")
+                .put(Phone.TYPE, Phone.TYPE_OTHER_FAX);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "130")
+                .put(Phone.TYPE, Phone.TYPE_TELEX);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "140")
+                .put(Phone.TYPE, Phone.TYPE_WORK_MOBILE);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "150")
+                .put(Phone.TYPE, Phone.TYPE_WORK_PAGER);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "160")
+                .put(Phone.TYPE, Phone.TYPE_MMS);
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "10", new TypeSet("HOME"))
+                .addExpectedNode("TEL", "20", new TypeSet("WORK"))
+                .addExpectedNode("TEL", "30", new TypeSet("HOME", "FAX"))
+                .addExpectedNode("TEL", "40", new TypeSet("WORK", "FAX"))
+                .addExpectedNode("TEL", "50", new TypeSet("CELL"))
+                .addExpectedNode("TEL", "60", new TypeSet("PAGER"))
+                .addExpectedNode("TEL", "70", new TypeSet("VOICE"))
+                .addExpectedNode("TEL", "80", new TypeSet("CAR"))
+                .addExpectedNode("TEL", "90", new TypeSet("WORK", "PREF"))
+                .addExpectedNode("TEL", "100", new TypeSet("ISDN"))
+                .addExpectedNode("TEL", "110", new TypeSet("PREF"))
+                .addExpectedNode("TEL", "120", new TypeSet("FAX"))
+                .addExpectedNode("TEL", "130", new TypeSet("TLX"))
+                .addExpectedNode("TEL", "140", new TypeSet("WORK", "CELL"))
+                .addExpectedNode("TEL", "150", new TypeSet("WORK", "PAGER"))
+                .addExpectedNode("TEL", "160", new TypeSet("MSG"));
+    }
+
+    public void testPhoneVariousTypeSupportV21() {
+        testPhoneVariousTypeSupport(V21);
+    }
+
+    public void testPhoneVariousTypeSupportV30() {
+        testPhoneVariousTypeSupport(V30);
+    }
+
+    /**
+     * Tests that "PREF"s are emitted appropriately.
+     */
+    private void testPhonePrefHandlingCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "1")
+                .put(Phone.TYPE, Phone.TYPE_HOME);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "2")
+                .put(Phone.TYPE, Phone.TYPE_WORK)
+                .put(Phone.IS_PRIMARY, 1);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "3")
+                .put(Phone.TYPE, Phone.TYPE_FAX_HOME)
+                .put(Phone.IS_PRIMARY, 1);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "4")
+                .put(Phone.TYPE, Phone.TYPE_FAX_WORK);
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "4", new TypeSet("WORK", "FAX"))
+                .addExpectedNode("TEL", "3", new TypeSet("HOME", "FAX", "PREF"))
+                .addExpectedNode("TEL", "2", new TypeSet("WORK", "PREF"))
+                .addExpectedNode("TEL", "1", new TypeSet("HOME"));
+    }
+
+    public void testPhonePrefHandlingV21() {
+        testPhonePrefHandlingCommon(V21);
+    }
+
+    public void testPhonePrefHandlingV30() {
+        testPhonePrefHandlingCommon(V30);
+    }
+
+    private void testMiscPhoneTypeHandling(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "1")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "Modem");
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "2")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "MSG");
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "3")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "BBS");
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "4")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "VIDEO");
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "5")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "6")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "_AUTO_CELL");  // The old indicator for the type mobile.
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "7")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "\u643A\u5E2F");  // Mobile phone in Japanese Kanji
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "8")
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "invalid");
+        PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
+        elem.addExpectedNode("TEL", "1", new TypeSet("MODEM"))
+                .addExpectedNode("TEL", "2", new TypeSet("MSG"))
+                .addExpectedNode("TEL", "3", new TypeSet("BBS"))
+                .addExpectedNode("TEL", "4", new TypeSet("VIDEO"))
+                .addExpectedNode("TEL", "5", new TypeSet("VOICE"))
+                .addExpectedNode("TEL", "6", new TypeSet("CELL"))
+                .addExpectedNode("TEL", "7", new TypeSet("CELL"))
+                .addExpectedNode("TEL", "8", new TypeSet("X-invalid"));
+    }
+
+    public void testPhoneTypeHandlingV21() {
+        testMiscPhoneTypeHandling(V21);
+    }
+
+    public void testPhoneTypeHandlingV30() {
+        testMiscPhoneTypeHandling(V30);
+    }
+
+    private void testEmailBasicCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "sample@example.com");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+            .addExpectedNode("EMAIL", "sample@example.com");
+    }
+
+    public void testEmailBasicV21() {
+        testEmailBasicCommon(V21);
+    }
+
+    public void testEmailBasicV30() {
+        testEmailBasicCommon(V30);
+    }
+
+    private void testEmailVariousTypeSupportCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "type_home@example.com")
+                .put(Email.TYPE, Email.TYPE_HOME);
+        entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "type_work@example.com")
+                .put(Email.TYPE, Email.TYPE_WORK);
+        entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "type_mobile@example.com")
+                .put(Email.TYPE, Email.TYPE_MOBILE);
+        entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "type_other@example.com")
+                .put(Email.TYPE, Email.TYPE_OTHER);
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME"))
+                .addExpectedNode("EMAIL", "type_work@example.com", new TypeSet("WORK"))
+                .addExpectedNode("EMAIL", "type_mobile@example.com", new TypeSet("CELL"))
+                .addExpectedNode("EMAIL", "type_other@example.com");
+    }
+
+    public void testEmailVariousTypeSupportV21() {
+        testEmailVariousTypeSupportCommon(V21);
+    }
+
+    public void testEmailVariousTypeSupportV30() {
+        testEmailVariousTypeSupportCommon(V30);
+    }
+
+    private void testEmailPrefHandlingCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "type_home@example.com")
+                .put(Email.TYPE, Email.TYPE_HOME)
+                .put(Email.IS_PRIMARY, 1);
+        entry.addContentValues(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "type_notype@example.com")
+                .put(Email.IS_PRIMARY, 1);
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("EMAIL", "type_notype@example.com", new TypeSet("PREF"))
+                .addExpectedNode("EMAIL", "type_home@example.com", new TypeSet("HOME", "PREF"));
+    }
+
+    public void testEmailPrefHandlingV21() {
+        testEmailPrefHandlingCommon(V21);
+    }
+
+    public void testEmailPrefHandlingV30() {
+        testEmailPrefHandlingCommon(V30);
+    }
+
+    private void testPostalAddressCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.POBOX, "Pobox")
+                .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood")
+                .put(StructuredPostal.STREET, "Street")
+                .put(StructuredPostal.CITY, "City")
+                .put(StructuredPostal.REGION, "Region")
+                .put(StructuredPostal.POSTCODE, "100")
+                .put(StructuredPostal.COUNTRY, "Country")
+                .put(StructuredPostal.FORMATTED_ADDRESS, "Formatted Address")
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK);
+        // adr-value    = 0*6(text-value ";") text-value
+        //              ; PO Box, Extended Address, Street, Locality, Region, Postal Code,
+        //              ; Country Name
+        //
+        // The NEIGHBORHOOD field is appended after the CITY field.
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("ADR",
+                        Arrays.asList("Pobox", "", "Street", "City Neighborhood",
+                                "Region", "100", "Country"), new TypeSet("WORK"));
+    }
+
+    public void testPostalAddressV21() {
+        testPostalAddressCommon(V21);
+    }
+
+    public void testPostalAddressV30() {
+        testPostalAddressCommon(V30);
+    }
+
+    private void testPostalAddressNonNeighborhood(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.CITY, "City");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("ADR",
+                        Arrays.asList("", "", "", "City", "", "", ""), new TypeSet("HOME"));
+    }
+
+    public void testPostalAddressNonNeighborhoodV21() {
+        testPostalAddressNonNeighborhood(V21);
+    }
+
+    public void testPostalAddressNonNeighborhoodV30() {
+        testPostalAddressNonNeighborhood(V30);
+    }
+
+    private void testPostalAddressNonCity(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.NEIGHBORHOOD, "Neighborhood");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("ADR",
+                        Arrays.asList("", "", "", "Neighborhood", "", "", ""), new TypeSet("HOME"));
+    }
+
+    public void testPostalAddressNonCityV21() {
+        testPostalAddressNonCity(V21);
+    }
+
+    public void testPostalAddressNonCityV30() {
+        testPostalAddressNonCity(V30);
+    }
+
+    private void testPostalOnlyWithFormattedAddressCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.REGION, "")  // Must be ignored.
+                .put(StructuredPostal.FORMATTED_ADDRESS,
+                "Formatted address CA 123-334 United Statue");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNodeWithOrder("ADR", ";Formatted address CA 123-334 United Statue;;;;;",
+                        Arrays.asList("", "Formatted address CA 123-334 United Statue",
+                                "", "", "", "", ""), new TypeSet("HOME"));
+    }
+
+    public void testPostalOnlyWithFormattedAddressV21() {
+        testPostalOnlyWithFormattedAddressCommon(V21);
+    }
+
+    public void testPostalOnlyWithFormattedAddressV30() {
+        testPostalOnlyWithFormattedAddressCommon(V30);
+    }
+
+    /**
+     * Tests that the vCard composer honors formatted data when it is available
+     * even when it is partial.
+     */
+    private void testPostalWithBothStructuredAndFormattedCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.POBOX, "Pobox")
+                .put(StructuredPostal.COUNTRY, "Country")
+                .put(StructuredPostal.FORMATTED_ADDRESS,
+                        "Formatted address CA 123-334 United Statue");  // Should be ignored
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("ADR", "Pobox;;;;;;Country",
+                        Arrays.asList("Pobox", "", "", "", "", "", "Country"),
+                        new TypeSet("HOME"));
+    }
+
+    public void testPostalWithBothStructuredAndFormattedV21() {
+        testPostalWithBothStructuredAndFormattedCommon(V21);
+    }
+
+    public void testPostalWithBothStructuredAndFormattedV30() {
+        testPostalWithBothStructuredAndFormattedCommon(V30);
+    }
+
+    private void testOrganizationCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "CompanyX")
+                .put(Organization.DEPARTMENT, "DepartmentY")
+                .put(Organization.TITLE, "TitleZ")
+                .put(Organization.JOB_DESCRIPTION, "Description Rambda")  // Ignored.
+                .put(Organization.OFFICE_LOCATION, "Mountain View")  // Ignored.
+                .put(Organization.PHONETIC_NAME, "PhoneticName!")  // Ignored
+                .put(Organization.SYMBOL, "(^o^)/~~");  // Ignore him (her).
+        entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
+                .putNull(Organization.COMPANY)
+                .put(Organization.DEPARTMENT, "DepartmentXX")
+                .putNull(Organization.TITLE);
+        entry.addContentValues(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "CompanyXYZ")
+                .putNull(Organization.DEPARTMENT)
+                .put(Organization.TITLE, "TitleXYZYX");
+        // Currently we do not use group but depend on the order.
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNodeWithOrder("ORG", "CompanyX;DepartmentY",
+                        Arrays.asList("CompanyX", "DepartmentY"))
+                .addExpectedNodeWithOrder("TITLE", "TitleZ")
+                .addExpectedNodeWithOrder("ORG", "DepartmentXX")
+                .addExpectedNodeWithOrder("ORG", "CompanyXYZ")
+                .addExpectedNodeWithOrder("TITLE", "TitleXYZYX");
+    }
+
+    public void testOrganizationV21() {
+        testOrganizationCommon(V21);
+    }
+
+    public void testOrganizationV30() {
+        testOrganizationCommon(V30);
+    }
+
+    private void testImVariousTypeSupportCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_AIM)
+                .put(Im.DATA, "aim");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_MSN)
+                .put(Im.DATA, "msn");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_YAHOO)
+                .put(Im.DATA, "yahoo");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_SKYPE)
+                .put(Im.DATA, "skype");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_QQ)
+                .put(Im.DATA, "qq");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_GOOGLE_TALK)
+                .put(Im.DATA, "google talk");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_ICQ)
+                .put(Im.DATA, "icq");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_JABBER)
+                .put(Im.DATA, "jabber");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_NETMEETING)
+                .put(Im.DATA, "netmeeting");
+
+        // No determined way to express unknown type...
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("X-JABBER", "jabber")
+                .addExpectedNode("X-ICQ", "icq")
+                .addExpectedNode("X-GOOGLE-TALK", "google talk")
+                .addExpectedNode("X-QQ", "qq")
+                .addExpectedNode("X-SKYPE-USERNAME", "skype")
+                .addExpectedNode("X-YAHOO", "yahoo")
+                .addExpectedNode("X-MSN", "msn")
+                .addExpectedNode("X-NETMEETING", "netmeeting")
+                .addExpectedNode("X-AIM", "aim");
+    }
+
+    public void testImBasiV21() {
+        testImVariousTypeSupportCommon(V21);
+    }
+
+    public void testImBasicV30() {
+        testImVariousTypeSupportCommon(V30);
+    }
+
+    private void testImPrefHandlingCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_AIM)
+                .put(Im.DATA, "aim1");
+        entry.addContentValues(Im.CONTENT_ITEM_TYPE)
+                .put(Im.PROTOCOL, Im.PROTOCOL_AIM)
+                .put(Im.DATA, "aim2")
+                .put(Im.TYPE, Im.TYPE_HOME)
+                .put(Im.IS_PRIMARY, 1);
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("X-AIM", "aim1")
+                .addExpectedNode("X-AIM", "aim2", new TypeSet("HOME", "PREF"));
+    }
+
+    public void testImPrefHandlingV21() {
+        testImPrefHandlingCommon(V21);
+    }
+
+    public void testImPrefHandlingV30() {
+        testImPrefHandlingCommon(V30);
+    }
+
+    private void testWebsiteCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Website.CONTENT_ITEM_TYPE)
+                .put(Website.URL, "http://website.example.android.com/index.html")
+                .put(Website.TYPE, Website.TYPE_BLOG);
+        entry.addContentValues(Website.CONTENT_ITEM_TYPE)
+                .put(Website.URL, "ftp://ftp.example.android.com/index.html")
+                .put(Website.TYPE, Website.TYPE_FTP);
+
+        // We drop TYPE information since vCard (especially 3.0) does not allow us to emit it.
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("URL", "ftp://ftp.example.android.com/index.html")
+                .addExpectedNode("URL", "http://website.example.android.com/index.html");
+    }
+
+    public void testWebsiteV21() {
+        testWebsiteCommon(V21);
+    }
+
+    public void testWebsiteV30() {
+        testWebsiteCommon(V30);
+    }
+
+    private String getAndroidPropValue(final String mimeType, String value, Integer type) {
+        return getAndroidPropValue(mimeType, value, type, null);
+    }
+
+    private String getAndroidPropValue(final String mimeType, String value,
+            Integer type, String label) {
+        return (mimeType + ";" + value + ";"
+                + (type != null ? type : "") + ";"
+                + (label != null ? label : "") + ";;;;;;;;;;;;");
+    }
+
+    private void testEventCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+                .put(Event.TYPE, Event.TYPE_ANNIVERSARY)
+                .put(Event.START_DATE, "1982-06-16");
+        entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+                .put(Event.TYPE, Event.TYPE_BIRTHDAY)
+                .put(Event.START_DATE, "2008-10-22");
+        entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+                .put(Event.TYPE, Event.TYPE_OTHER)
+                .put(Event.START_DATE, "2018-03-12");
+        entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+                .put(Event.TYPE, Event.TYPE_CUSTOM)
+                .put(Event.LABEL, "The last day")
+                .put(Event.START_DATE, "When the Tower of Hanoi with 64 rings is completed.");
+        entry.addContentValues(Event.CONTENT_ITEM_TYPE)
+                .put(Event.TYPE, Event.TYPE_BIRTHDAY)
+                .put(Event.START_DATE, "2009-05-19");  // Should be ignored.
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("BDAY", "2008-10-22")
+                .addExpectedNode("X-ANDROID-CUSTOM",
+                        getAndroidPropValue(
+                                Event.CONTENT_ITEM_TYPE, "1982-06-16", Event.TYPE_ANNIVERSARY))
+                .addExpectedNode("X-ANDROID-CUSTOM",
+                        getAndroidPropValue(
+                                Event.CONTENT_ITEM_TYPE, "2018-03-12", Event.TYPE_OTHER))
+                .addExpectedNode("X-ANDROID-CUSTOM",
+                        getAndroidPropValue(
+                                Event.CONTENT_ITEM_TYPE,
+                                "When the Tower of Hanoi with 64 rings is completed.",
+                                Event.TYPE_CUSTOM, "The last day"));
+    }
+
+    public void testEventV21() {
+        testEventCommon(V21);
+    }
+
+    public void testEventV30() {
+        testEventCommon(V30);
+    }
+
+    private void testNoteCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "note1");
+        entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "note2")
+                .put(Note.IS_PRIMARY, 1);  // Just ignored.
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNodeWithOrder("NOTE", "note1")
+                .addExpectedNodeWithOrder("NOTE", "note2");
+    }
+
+    public void testNoteV21() {
+        testNoteCommon(V21);
+    }
+
+    public void testNoteV30() {
+        testNoteCommon(V30);
+    }
+
+    private void testPhotoCommon(int vcardType) {
+        final boolean isV30 = vcardType == V30;
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "PhotoTest");
+        entry.addContentValues(Photo.CONTENT_ITEM_TYPE)
+                .put(Photo.PHOTO, sPhotoByteArray);
+
+        ContentValues contentValuesForPhoto = new ContentValues();
+        contentValuesForPhoto.put("ENCODING", (isV30 ? "b" : "BASE64"));
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("FN", "PhotoTest")
+                .addExpectedNode("N", "PhotoTest;;;;",
+                        Arrays.asList("PhotoTest", "", "", "", ""))
+                .addExpectedNodeWithOrder("PHOTO", null, null, sPhotoByteArray,
+                        contentValuesForPhoto, new TypeSet("JPEG"), null);
+    }
+
+    public void testPhotoV21() {
+        testPhotoCommon(V21);
+    }
+
+    public void testPhotoV30() {
+        testPhotoCommon(V30);
+    }
+
+    private void testRelationCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        mVerifier.addInputEntry().addContentValues(Relation.CONTENT_ITEM_TYPE)
+                .put(Relation.TYPE, Relation.TYPE_MOTHER)
+                .put(Relation.NAME, "Ms. Mother");
+        mVerifier.addContentValuesVerifierElem().addExpected(Relation.CONTENT_ITEM_TYPE)
+                .put(Relation.TYPE, Relation.TYPE_MOTHER)
+                .put(Relation.NAME, "Ms. Mother");
+    }
+
+    public void testRelationV21() {
+        testRelationCommon(V21);
+    }
+
+    public void testRelationV30() {
+        testRelationCommon(V30);
+    }
+
+    public void testV30HandleEscape() {
+        mVerifier.initForExportTest(V30);
+        mVerifier.addInputEntry().addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\\")
+                .put(StructuredName.GIVEN_NAME, ";")
+                .put(StructuredName.MIDDLE_NAME, ",")
+                .put(StructuredName.PREFIX, "\n")
+                .put(StructuredName.DISPLAY_NAME, "[<{Unescaped:Asciis}>]");
+        // Verifies the vCard String correctly escapes each character which must be escaped.
+        mVerifier.addLineVerifierElem()
+                .addExpected("N:\\\\;\\;;\\,;\\n;")
+                .addExpected("FN:[<{Unescaped:Asciis}>]");
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("FN", "[<{Unescaped:Asciis}>]")
+                .addExpectedNode("N", Arrays.asList("\\", ";", ",", "\n", ""));
+    }
+
+    /**
+     * There's no "NICKNAME" property in vCard 2.1, while there is in vCard 3.0.
+     * We use Android-specific "X-ANDROID-CUSTOM" property.
+     * This test verifies the functionality.
+     */
+    public void testNickNameV21() {
+        mVerifier.initForExportTest(V21);
+        mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
+                .put(Nickname.NAME, "Nicky");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("X-ANDROID-CUSTOM",
+                        Nickname.CONTENT_ITEM_TYPE + ";Nicky;;;;;;;;;;;;;;");
+        mVerifier.addContentValuesVerifierElem().addExpected(Nickname.CONTENT_ITEM_TYPE)
+                .put(Nickname.NAME, "Nicky");
+    }
+
+    public void testTolerateBrokenPhoneNumberEntryV21() {
+        mVerifier.initForExportTest(V21);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_HOME)
+                .put(Phone.NUMBER, "111-222-3333 (Miami)\n444-5555-666 (Tokyo);"
+                        + "777-888-9999 (Chicago);111-222-3333 (Miami)");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "111-222-3333", new TypeSet("HOME"))
+                .addExpectedNode("TEL", "444-555-5666", new TypeSet("HOME"))
+                .addExpectedNode("TEL", "777-888-9999", new TypeSet("HOME"));
+    }
+
+    private void testPickUpNonEmptyContentValuesCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.IS_PRIMARY, 1);  // Empty name. Should be ignored.
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "family1");  // Not primary. Should be ignored.
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.IS_PRIMARY, 1)
+                .put(StructuredName.FAMILY_NAME, "family2");  // This entry is what we want.
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.IS_PRIMARY, 1)
+                .put(StructuredName.FAMILY_NAME, "family3");
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "family4");
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("N", Arrays.asList("family2", "", "", "", ""))
+                .addExpectedNode("FN", "family2");
+    }
+
+    public void testPickUpNonEmptyContentValuesV21() {
+        testPickUpNonEmptyContentValuesCommon(V21);
+    }
+
+    public void testPickUpNonEmptyContentValuesV30() {
+        testPickUpNonEmptyContentValuesCommon(V30);
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
new file mode 100644
index 0000000..21f2254
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardImporterTests.java
@@ -0,0 +1,1011 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardConfig;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Event;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+
+import com.android.frameworks.coretests.R;
+import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
+
+import java.util.Arrays;
+
+public class VCardImporterTests extends VCardTestsBase {
+    // Push data into int array at first since values like 0x80 are
+    // interpreted as int by the compiler and casting all of them is
+    // cumbersome...
+    private static final int[] sPhotoIntArrayForComplicatedCase = {
+        0xff, 0xd8, 0xff, 0xe1, 0x0a, 0x0f, 0x45, 0x78, 0x69, 0x66, 0x00,
+        0x00, 0x4d, 0x4d, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0d,
+        0x01, 0x0e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+        0xaa, 0x01, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+        0x00, 0xba, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00,
+        0x00, 0x00, 0xc2, 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+        0x00, 0x01, 0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00, 0xc8, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00, 0x00, 0xd0, 0x01, 0x28, 0x00, 0x03, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x31, 0x00, 0x02,
+        0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x32, 0x00,
+        0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xe6, 0x02, 0x13,
+        0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x82,
+        0x98, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0xfa,
+        0x87, 0x69, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
+        0x84, 0xc4, 0xa5, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00,
+        0x01, 0x08, 0x00, 0x00, 0x04, 0x1e, 0x32, 0x30, 0x30, 0x38, 0x31,
+        0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31, 0x00, 0x00,
+        0x44, 0x6f, 0x43, 0x6f, 0x4d, 0x6f, 0x00, 0x00, 0x44, 0x39, 0x30,
+        0x35, 0x69, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01,
+        0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x44, 0x39, 0x30,
+        0x35, 0x69, 0x20, 0x56, 0x65, 0x72, 0x31, 0x2e, 0x30, 0x30, 0x00,
+        0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+        0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x20, 0x20,
+        0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+        0x00, 0x50, 0x72, 0x69, 0x6e, 0x74, 0x49, 0x4d, 0x00, 0x30, 0x33,
+        0x30, 0x30, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x14, 0x00,
+        0x14, 0x00, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00,
+        0x00, 0x34, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
+        0x00, 0x00, 0x00, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x11, 0x09, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x0f, 0x0b, 0x00,
+        0x00, 0x27, 0x10, 0x00, 0x00, 0x05, 0x97, 0x00, 0x00, 0x27, 0x10,
+        0x00, 0x00, 0x08, 0xb0, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1c,
+        0x01, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x02, 0x5e, 0x00, 0x00,
+        0x27, 0x10, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x27, 0x10, 0x00,
+        0x00, 0x03, 0xcb, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x1b, 0xe5,
+        0x00, 0x00, 0x27, 0x10, 0x00, 0x28, 0x82, 0x9a, 0x00, 0x05, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x6a, 0x82, 0x9d, 0x00, 0x05,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0x72, 0x88, 0x22, 0x00,
+        0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x90, 0x00,
+        0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30, 0x32, 0x32, 0x30, 0x90,
+        0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03, 0x7a,
+        0x90, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x03,
+        0x8e, 0x91, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x02,
+        0x03, 0x00, 0x91, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x03, 0xa2, 0x92, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01,
+        0x00, 0x00, 0x03, 0xaa, 0x92, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x03, 0xb2, 0x92, 0x04, 0x00, 0x0a, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00, 0x03, 0xba, 0x92, 0x05, 0x00, 0x05, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xc2, 0x92, 0x07, 0x00, 0x03,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x92, 0x08, 0x00,
+        0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92, 0x09,
+        0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x92,
+        0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xca,
+        0x92, 0x7c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x00, 0x92, 0x86, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00,
+        0x03, 0xd2, 0xa0, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x04, 0x30,
+        0x31, 0x30, 0x30, 0xa0, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+        0x00, 0x01, 0x00, 0x00, 0xa0, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x60, 0x00, 0x00, 0xa0, 0x03, 0x00, 0x03, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x48, 0x00, 0x00, 0xa0, 0x05, 0x00, 0x04, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0xa2, 0x0e, 0x00, 0x05,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xe8, 0xa2, 0x0f, 0x00,
+        0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x03, 0xf0, 0xa2, 0x10,
+        0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0xa2,
+        0x17, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00,
+        0xa3, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00,
+        0x00, 0xa3, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
+        0x00, 0x00, 0xa4, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0xa4, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+        0x00, 0x00, 0x00, 0x00, 0xa4, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x04, 0x00, 0x05, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00, 0x03, 0xf8, 0xa4, 0x05, 0x00, 0x03, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x00, 0xa4, 0x06, 0x00, 0x03,
+        0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x07, 0x00,
+        0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4, 0x08,
+        0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xa4,
+        0x09, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0xa4, 0x0a, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x00, 0xa4, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00,
+        0x00, 0x27, 0x10, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64,
+        0x32, 0x30, 0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20,
+        0x31, 0x33, 0x3a, 0x35, 0x35, 0x3a, 0x33, 0x31, 0x00, 0x32, 0x30,
+        0x30, 0x38, 0x3a, 0x31, 0x30, 0x3a, 0x32, 0x39, 0x20, 0x31, 0x33,
+        0x3a, 0x35, 0x35, 0x3a, 0x34, 0x37, 0x00, 0x00, 0x00, 0x29, 0x88,
+        0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x02, 0xb2, 0x00, 0x00, 0x00,
+        0x64, 0x00, 0x00, 0x01, 0x5e, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x25, 0x00,
+        0x00, 0x00, 0x0a, 0x00, 0x00, 0x0e, 0x92, 0x00, 0x00, 0x03, 0xe8,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x30, 0x30,
+        0x38, 0x31, 0x30, 0x32, 0x39, 0x31, 0x33, 0x35, 0x35, 0x33, 0x31,
+        0x00, 0x00, 0x20, 0x2a, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x2a,
+        0xe2, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00,
+        0x04, 0x52, 0x39, 0x38, 0x00, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00,
+        0x00, 0x04, 0x30, 0x31, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x06, 0x01, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x06,
+        0x00, 0x00, 0x01, 0x1a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x00, 0x04, 0x6c, 0x01, 0x1b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01,
+        0x00, 0x00, 0x04, 0x74, 0x01, 0x28, 0x00, 0x03, 0x00, 0x00, 0x00,
+        0x01, 0x00, 0x02, 0x00, 0x00, 0x02, 0x01, 0x00, 0x04, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00, 0x04, 0x7c, 0x02, 0x02, 0x00, 0x04, 0x00,
+        0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x8b, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x48, 0x00, 0x00, 0x00, 0x01, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84,
+        0x00, 0x20, 0x16, 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c,
+        0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, 0x2c, 0x2c, 0x30,
+        0x62, 0x46, 0x4a, 0x3a, 0x50, 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66,
+        0x70, 0x6e, 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, 0x6e,
+        0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, 0xce, 0xd0, 0xce, 0x7c,
+        0x9a, 0xe2, 0xf2, 0xe0, 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0x01,
+        0x22, 0x24, 0x24, 0x30, 0x2a, 0x30, 0x5e, 0x34, 0x34, 0x5e, 0xc6,
+        0x84, 0x70, 0x84, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+        0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+        0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+        0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+        0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xff, 0xc0,
+        0x00, 0x11, 0x08, 0x00, 0x78, 0x00, 0xa0, 0x03, 0x01, 0x21, 0x00,
+        0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00,
+        0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+        0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03,
+        0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01,
+        0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
+        0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81,
+        0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+        0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19,
+        0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37,
+        0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+        0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+        0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+        0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92,
+        0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4,
+        0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+        0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+        0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+        0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1,
+        0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00,
+        0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+        0x07, 0x08, 0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+        0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
+        0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12,
+        0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14,
+        0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15,
+        0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17,
+        0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37,
+        0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a,
+        0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+        0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+        0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a,
+        0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
+        0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5,
+        0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+        0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+        0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2,
+        0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00,
+        0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
+        0x14, 0x54, 0xaa, 0x2a, 0x46, 0x48, 0xa2, 0xa4, 0x55, 0xa6, 0x04,
+        0x8a, 0x29, 0xe0, 0x53, 0x10, 0xe0, 0x29, 0xc0, 0x50, 0x03, 0xb1,
+        0x46, 0x29, 0x80, 0x84, 0x52, 0x11, 0x40, 0x0d, 0x22, 0x9a, 0x45,
+        0x20, 0x23, 0x61, 0x51, 0x30, 0xa0, 0x08, 0xc8, 0xa8, 0xd8, 0x52,
+        0x02, 0x26, 0x15, 0x0b, 0x0a, 0x00, 0xb4, 0xa2, 0xa5, 0x5a, 0x00,
+        0x91, 0x45, 0x4a, 0xa2, 0x81, 0x92, 0x01, 0x4e, 0x02, 0x98, 0x87,
+        0x0a, 0x70, 0xa0, 0x07, 0x62, 0x8c, 0x50, 0x21, 0x0d, 0x25, 0x00,
+        0x34, 0x8a, 0x61, 0x14, 0x0c, 0x63, 0x0a, 0x89, 0x85, 0x00, 0x46,
+        0xd5, 0x1b, 0x52, 0x02, 0x16, 0xa8, 0x98, 0x50, 0x05, 0x94, 0xa9,
+        0x16, 0x80, 0x25, 0x5a, 0x95, 0x68, 0x18, 0xf1, 0x4f, 0x14, 0xc4,
+        0x3b, 0xb5, 0x22, 0xb6, 0x38, 0x34, 0x00, 0xe3, 0x22, 0x8e, 0xf4,
+        0x79, 0x8a, 0x7b, 0xd1, 0x71, 0x03, 0x30, 0xc7, 0x14, 0x83, 0xa5,
+        0x00, 0x06, 0x98, 0x68, 0x01, 0x8d, 0x51, 0x35, 0x03, 0x22, 0x6a,
+        0x8d, 0xa9, 0x01, 0x13, 0x54, 0x4d, 0x40, 0x13, 0xa5, 0x4a, 0x28,
+        0x02, 0x45, 0x35, 0x2a, 0x9a, 0x00, 0x78, 0x34, 0xf0, 0x69, 0x80,
+        0x34, 0x81, 0x45, 0x40, 0xce, 0x58, 0xe6, 0xa2, 0x4c, 0x06, 0xe4,
+        0xfa, 0xd1, 0x93, 0x50, 0x21, 0xca, 0xe4, 0x55, 0x84, 0x90, 0x30,
+        0xab, 0x8b, 0x18, 0xa6, 0x9a, 0x6a, 0xc4, 0x31, 0xaa, 0x26, 0xa0,
+        0x64, 0x4d, 0x51, 0xb5, 0x20, 0x23, 0x6a, 0x89, 0xa8, 0x02, 0x44,
+        0x35, 0x2a, 0x9a, 0x00, 0x95, 0x4d, 0x48, 0xa6, 0x80, 0x24, 0x53,
+        0x4e, 0xce, 0x05, 0x30, 0x2b, 0x3b, 0xee, 0x6a, 0x91, 0x5d, 0x76,
+        0x63, 0xbd, 0x65, 0x7d, 0x40, 0x66, 0x68, 0xa9, 0x02, 0x45, 0x2b,
+        0xb3, 0x9e, 0xb4, 0xc5, 0x6d, 0xad, 0x9a, 0xa0, 0x2c, 0x06, 0xc8,
+        0xcd, 0x04, 0xd6, 0xa2, 0x23, 0x63, 0x51, 0xb1, 0xa0, 0x64, 0x4d,
+        0x51, 0x93, 0x48, 0x08, 0xda, 0xa2, 0x6a, 0x00, 0x72, 0x1a, 0x99,
+        0x4d, 0x00, 0x48, 0xa6, 0xa4, 0x53, 0x4c, 0x07, 0x86, 0x03, 0xbd,
+        0x2b, 0x9c, 0xa7, 0x14, 0x98, 0x10, 0x85, 0x34, 0xe0, 0xa6, 0xb3,
+        0xb0, 0x0b, 0xb5, 0xa8, 0x0a, 0xd4, 0x58, 0x42, 0xed, 0x3e, 0x94,
+        0xd2, 0xa6, 0x8b, 0x01, 0x34, 0x44, 0xed, 0xe6, 0x9c, 0x4d, 0x6a,
+        0x80, 0x8d, 0x8d, 0x46, 0xc6, 0x80, 0x23, 0x63, 0x51, 0x9a, 0x06,
+        0x46, 0xd5, 0x13, 0x52, 0x01, 0x54, 0xd4, 0xaa, 0x68, 0x02, 0x40,
+        0x6a, 0x40, 0x78, 0xa0, 0x08, 0x59, 0xce, 0xee, 0xb5, 0x2a, 0x39,
+        0xd9, 0x59, 0xa7, 0xa8, 0x00, 0x73, 0xeb, 0x4e, 0x0e, 0x7d, 0x69,
+        0x5c, 0x05, 0xf3, 0x0f, 0xad, 0x1e, 0x61, 0xf5, 0xa7, 0x71, 0x0b,
+        0xe6, 0x35, 0x21, 0x90, 0xd3, 0xb8, 0x0e, 0x32, 0x10, 0x95, 0x10,
+        0x91, 0xb3, 0xd6, 0x9b, 0x60, 0x4b, 0x9c, 0x8a, 0x63, 0x1a, 0xb0,
+        0x18, 0x4d, 0x46, 0xc6, 0x80, 0x22, 0x6a, 0x61, 0xa4, 0x31, 0xaa,
+        0x6a, 0x55, 0x34, 0x01, 0x2a, 0x9a, 0x7e, 0x78, 0xa0, 0x08, 0x09,
+        0xf9, 0xaa, 0x58, 0xcf, 0xca, 0x6b, 0x3e, 0xa0, 0x00, 0xd3, 0x81,
+        0xa9, 0x01, 0x73, 0x46, 0x69, 0x80, 0xb9, 0xa4, 0xcd, 0x00, 0x2b,
+        0x1f, 0x92, 0xa3, 0x07, 0x9a, 0x6f, 0x70, 0x26, 0xcf, 0x14, 0xd2,
+        0x6b, 0x51, 0x0c, 0x63, 0x51, 0xb1, 0xa0, 0x08, 0xda, 0x98, 0x69,
+        0x0c, 0x8d, 0x4d, 0x4a, 0xa6, 0x80, 0x24, 0x53, 0x52, 0x03, 0xc5,
+        0x02, 0x21, 0x27, 0xe6, 0xa9, 0x23, 0x3f, 0x29, 0xac, 0xfa, 0x8c,
+        0x01, 0xe6, 0x9c, 0x0d, 0x48, 0x0a, 0x0d, 0x2e, 0x68, 0x01, 0x73,
+        0x49, 0x9a, 0x60, 0x2b, 0x1f, 0x92, 0x98, 0x3a, 0xd3, 0x7b, 0x81,
+        0x36, 0x78, 0xa6, 0x93, 0x5a, 0x88, 0x8c, 0x9a, 0x63, 0x1a, 0x00,
+        0x8c, 0xd3, 0x0d, 0x21, 0x91, 0x29, 0xa9, 0x14, 0xd0, 0x04, 0x8a,
+        0x69, 0xe0, 0xd3, 0x11, 0x1b, 0x1e, 0x6a, 0x48, 0xcf, 0xca, 0x6b,
+        0x3e, 0xa3, 0x10, 0x1a, 0x70, 0x35, 0x20, 0x38, 0x1a, 0x5c, 0xd2,
+        0x01, 0x73, 0x49, 0x9a, 0x60, 0x39, 0x8f, 0xca, 0x29, 0x8b, 0xf7,
+        0xaa, 0xba, 0x88, 0x96, 0x9a, 0x6b, 0x40, 0x18, 0xc6, 0xa3, 0x26,
+        0x80, 0x18, 0x69, 0xa6, 0x90, 0xc8, 0x14, 0xd4, 0x8a, 0x69, 0x80,
+        0xf0, 0x6a, 0x40, 0x68, 0x10, 0xbb, 0x41, 0xa7, 0xe3, 0x0b, 0xc5,
+        0x2b, 0x01, 0x10, 0xa7, 0x03, 0x59, 0x0c, 0x76, 0x69, 0x73, 0x40,
+        0x0b, 0x9a, 0x28, 0x11, 0x28, 0x19, 0x5e, 0x69, 0x02, 0x81, 0x5a,
+        0xd8, 0x00, 0xd3, 0x4d, 0x50, 0x0c, 0x6a, 0x8c, 0xd2, 0x01, 0xa6,
+        0x98, 0x69, 0x0c, 0xae, 0xa6, 0xa4, 0x06, 0x80, 0x1e, 0xa6, 0x9e,
+        0x0d, 0x31, 0x12, 0x03, 0x4f, 0x06, 0x80, 0x13, 0x60, 0x34, 0xd3,
+        0xc1, 0xa8, 0x92, 0x01, 0xf1, 0x8d, 0xdd, 0x69, 0xcc, 0xa1, 0x69,
+        0x5b, 0x4b, 0x80, 0x83, 0x93, 0x52, 0x04, 0x14, 0xe2, 0xae, 0x03,
+        0xa9, 0x0d, 0x68, 0x03, 0x4d, 0x34, 0xd0, 0x03, 0x0d, 0x30, 0xd2,
+        0x01, 0x86, 0x9a, 0x68, 0x19, 0x58, 0x1a, 0x78, 0xa4, 0x04, 0x8a,
+        0x69, 0xe0, 0xd3, 0x10, 0xe0, 0x69, 0xe0, 0xd0, 0x03, 0xc1, 0xa8,
+        0xdb, 0xad, 0x4c, 0x81, 0x12, 0x45, 0xd6, 0x9d, 0x25, 0x1d, 0x00,
+        0x6a, 0xf5, 0xa9, 0xe8, 0x80, 0x31, 0x29, 0x0d, 0x58, 0x08, 0x69,
+        0x86, 0x80, 0x1a, 0x69, 0x86, 0x90, 0x0c, 0x34, 0xd3, 0x48, 0x65,
+        0x51, 0x4f, 0x06, 0x98, 0x0f, 0x14, 0xf0, 0x68, 0x10, 0xf0, 0x69,
+        0xe0, 0xd0, 0x03, 0x81, 0xa5, 0x2b, 0x9a, 0x1a, 0xb8, 0x87, 0xa8,
+        0xdb, 0x4a, 0x46, 0x68, 0xb6, 0x80, 0x2a, 0xa8, 0x14, 0xea, 0x12,
+        0xb0, 0x05, 0x21, 0xa6, 0x02, 0x1a, 0x61, 0xa0, 0x06, 0x9a, 0x61,
+        0xa4, 0x31, 0x86, 0x9a, 0x69, 0x0c, 0xa8, 0x0d, 0x3c, 0x53, 0x01,
+        0xe2, 0x9e, 0x28, 0x10, 0xf1, 0x4e, 0x06, 0x98, 0x0f, 0x06, 0x9e,
+        0x0d, 0x02, 0x1c, 0x29, 0xc2, 0x80, 0x16, 0x96, 0x80, 0x0a, 0x4a,
+        0x00, 0x43, 0x4d, 0x34, 0x0c, 0x61, 0xa6, 0x1a, 0x40, 0x34, 0xd3,
+        0x4d, 0x21, 0x80, 0xff, 0xd9, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x0a,
+        0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, 0x08, 0x08, 0x0b, 0x0a,
+        0x0a, 0x0b, 0x0e, 0x18, 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15,
+        0x16, 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, 0x22, 0x21,
+        0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, 0x34, 0x29, 0x21, 0x22, 0x30,
+        0x41, 0x31, 0x34, 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44,
+        0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, 0x01, 0x0a, 0x0b,
+        0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, 0x10, 0x1c, 0x3b, 0x28, 0x22,
+        0x28, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+        0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+        0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+        0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b,
+        0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0xff, 0xc0, 0x00, 0x11,
+        0x08, 0x00, 0x48, 0x00, 0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11,
+        0x01, 0x03, 0x11, 0x01, 0xff, 0xc4, 0x01, 0xa2, 0x00, 0x00, 0x01,
+        0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02,
+        0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01,
+        0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
+        0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1,
+        0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33,
+        0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25,
+        0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+        0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+        0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+        0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+        0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94,
+        0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+        0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8,
+        0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca,
+        0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+        0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3,
+        0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0x01, 0x00, 0x03, 0x01,
+        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0a, 0x0b, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03,
+        0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+        0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+        0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+        0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72,
+        0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19,
+        0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39,
+        0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54,
+        0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67,
+        0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a,
+        0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+        0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+        0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+        0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
+        0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2,
+        0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
+        0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xda, 0x00, 0x0c, 0x03,
+        0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00, 0x9e, 0xd2,
+        0x2e, 0x07, 0x15, 0xaf, 0x6d, 0x08, 0xe2, 0xb3, 0x45, 0x1a, 0xf6,
+        0xd0, 0x00, 0x01, 0xc5, 0x68, 0x45, 0x17, 0x4a, 0xb4, 0x22, 0xe4,
+        0x70, 0x8c, 0x74, 0xa9, 0x3c, 0xa1, 0x8e, 0x95, 0x48, 0x96, 0x31,
+        0xe2, 0x18, 0xe9, 0x55, 0xa5, 0x8c, 0x7a, 0x50, 0x05, 0x0b, 0x88,
+        0x86, 0x0f, 0x15, 0x8f, 0x75, 0x1f, 0x26, 0x93, 0x19, 0x91, 0x77,
+        0x18, 0xc1, 0xac, 0x4b, 0xc8, 0xfa, 0xd6, 0x63, 0x37, 0x6d, 0x31,
+        0xb4, 0x73, 0x5b, 0x36, 0xa0, 0x1c, 0x50, 0x80, 0xd7, 0x83, 0xa0,
+        0xab, 0xd1, 0x62, 0xad, 0x09, 0x8f, 0x17, 0x29, 0x03, 0xb2, 0xcc,
+        0xe0, 0x77, 0x14, 0xa3, 0x56, 0xb3, 0x27, 0x1e, 0x67, 0xe9, 0x52,
+        0xea, 0xc6, 0x3a, 0x36, 0x48, 0xef, 0x3d, 0x27, 0x70, 0x22, 0x60,
+        0x47, 0x52, 0x69, 0xb2, 0xe2, 0xad, 0x3b, 0xea, 0x80, 0xa3, 0x38,
+        0xe0, 0xd6, 0x3d, 0xd8, 0x1c, 0xd0, 0xca, 0x46, 0x3d, 0xd0, 0x18,
+        0x35, 0x89, 0x78, 0xa3, 0x9a, 0xcd, 0x8c, 0xd2, 0xb3, 0x93, 0x2a,
+        0x2b, 0x66, 0xd5, 0xf1, 0x8a, 0x10, 0x1a, 0xd6, 0xf2, 0x03, 0x8a,
+        0x9e, 0xe6, 0xf4, 0x5a, 0xdb, 0xef, 0xfe, 0x23, 0xc0, 0xa7, 0x27,
+        0xcb, 0x16, 0xc4, 0xcc, 0xdd, 0xe2, 0x78, 0x9a, 0x69, 0x66, 0xcc,
+        0x99, 0xe1, 0x4d, 0x47, 0xba, 0xbc, 0xd9, 0x6a, 0xee, 0x26, 0x59,
+        0x59, 0x4d, 0xac, 0x69, 0x34, 0x52, 0xe5, 0x8f, 0x55, 0xad, 0x58,
+        0xae, 0x85, 0xc4, 0x22, 0x41, 0xdf, 0xad, 0x76, 0x61, 0xe5, 0x6f,
+        0x74, 0x45, 0x69, 0xdc, 0x00, 0x79, 0xac, 0x8b, 0xa6, 0xc9, 0x35,
+        0xd4, 0x34, 0x64, 0xdc, 0x37, 0x06, 0xb1, 0xae, 0x88, 0xc1, 0xac,
+        0xd8, 0xc9, 0x2c, 0xa6, 0xe0, 0x73, 0x5b, 0x36, 0xf3, 0x74, 0xe6,
+        0x84, 0x05, 0xe3, 0xa9, 0x47, 0x6a, 0x14, 0xb6, 0x49, 0x3d, 0x85,
+        0x3a, 0xee, 0xee, 0x2b, 0xa8, 0xe2, 0x6f, 0x30, 0x81, 0xe9, 0x8a,
+        0xca, 0xa4, 0xe2, 0xd3, 0x8b, 0x01, 0xb1, 0xf9, 0x04, 0x7f, 0xaf,
+        0x23, 0xf0, 0xa9, 0x54, 0x41, 0x9c, 0xfd, 0xa3, 0xf4, 0xae, 0x65,
+        0x18, 0xf7, 0x25, 0x8a, 0xe2, 0x02, 0x38, 0xb8, 0xfd, 0x2a, 0x7b,
+        0x5b, 0xa8, 0x6d, 0x6d, 0x5d, 0x9a, 0x5d, 0xcb, 0xbb, 0xd2, 0xb6,
+        0xa6, 0xa3, 0x19, 0x5e, 0xe2, 0x03, 0x7b, 0x1d, 0xc2, 0x17, 0x8d,
+        0xb8, 0xac, 0xfb, 0x89, 0x39, 0x35, 0xd6, 0x9a, 0x6a, 0xe8, 0x66,
+        0x55, 0xcb, 0xf5, 0xac, 0x7b, 0x96, 0xeb, 0x50, 0xc6, 0x88, 0x6d,
+        0x66, 0xe9, 0xcd, 0x6c, 0xdb, 0x4f, 0xd3, 0x9a, 0x00, 0x2f, 0xe6,
+        0xf9, 0xa3, 0xe7, 0xb5, 0x4a, 0x93, 0x7f, 0xa2, 0xc6, 0x73, 0xdc,
+        0xd7, 0x15, 0x55, 0xef, 0x48, 0x7d, 0x09, 0x52, 0x6e, 0x3a, 0xd4,
+        0xab, 0x2f, 0xbd, 0x61, 0x16, 0x0c, 0x73, 0x49, 0xc5, 0x24, 0x92,
+        0x7f, 0xa2, 0x63, 0xfd, 0xaa, 0xd6, 0x2f, 0x71, 0x0e, 0xb1, 0x93,
+        0xf7, 0x2d, 0xf5, 0xa4, 0x9e, 0x4e, 0xb5, 0xdd, 0x4b, 0xf8, 0x68,
+        0x4c, 0xcb, 0xb9, 0x93, 0xad, 0x65, 0xce, 0xd9, 0x26, 0xa9, 0x8d,
+        0x19, 0xf6, 0xf2, 0xf4, 0xe6, 0xb5, 0xad, 0xe7, 0xc6, 0x39, 0xa0,
+        0x18, 0xeb, 0xc9, 0x77, 0x6c, 0x35, 0x2a, 0x4b, 0xfe, 0x8a, 0x9c,
+        0xff, 0x00, 0x11, 0xae, 0x3a, 0x8b, 0xde, 0x61, 0xd0, 0x9e, 0x39,
+        0xb8, 0xeb, 0x53, 0xac, 0xb9, 0xae, 0x5b, 0x00, 0xf3, 0x27, 0x14,
+        0x92, 0xc9, 0xfe, 0x8a, 0x3f, 0xde, 0x35, 0xac, 0x3a, 0x88, 0x92,
+        0xcd, 0xb1, 0x6e, 0x7d, 0xcd, 0x32, 0x67, 0xeb, 0xcd, 0x7a, 0x14,
+        0xfe, 0x04, 0x26, 0x66, 0xce, 0xf9, 0x26, 0xb3, 0xe6, 0x6e, 0xb4,
+        0xd9, 0x48, 0xc8, 0x82, 0x4e, 0x07, 0x35, 0xa7, 0x6f, 0x2f, 0x02,
+        0x9a, 0x06, 0x5f, 0x8c, 0xa4, 0x83, 0x0e, 0x32, 0x2a, 0x69, 0xe3,
+        0xdd, 0x12, 0x08, 0x97, 0x85, 0xec, 0x2a, 0x2a, 0x42, 0xf1, 0x76,
+        0x26, 0xe4, 0x6a, 0x59, 0x0e, 0x18, 0x10, 0x6a, 0xd2, 0x89, 0x02,
+        0x6e, 0x2a, 0x71, 0xeb, 0x5c, 0x1c, 0x8c, 0xa6, 0x48, 0xbb, 0xdc,
+        0x61, 0x41, 0x35, 0x72, 0x28, 0x87, 0xd9, 0xf6, 0x4a, 0xb9, 0xe7,
+        0x38, 0xae, 0x8c, 0x3d, 0x36, 0xdd, 0xde, 0xc4, 0xb0, 0x21, 0x51,
+        0x76, 0xa8, 0xc0, 0xaa, 0x93, 0x31, 0xe6, 0xbb, 0x2d, 0x65, 0x61,
+        0x19, 0xd3, 0x1e, 0xb5, 0x46, 0x5a, 0x96, 0x5a, 0x30, 0xa0, 0x7e,
+        0x05, 0x69, 0x5b, 0xc9, 0xc6, 0x28, 0x40, 0xcd, 0x08, 0x64, 0x3c,
+        0x73, 0x57, 0xe1, 0x94, 0xf1, 0xcd, 0x5a, 0x21, 0x8c, 0xb9, 0x63,
+        0xe7, 0x67, 0x1d, 0xab, 0x40, 0xb1, 0xfb, 0x00, 0x1d, 0xf0, 0x2b,
+        0x99, 0x2d, 0x66, 0x3e, 0x88, 0x75, 0x81, 0x3f, 0x31, 0xf6, 0xab,
+        0x64, 0xd6, 0xb4, 0x17, 0xee, 0xd0, 0x9e, 0xe4, 0x32, 0x1a, 0xa7,
+        0x31, 0xad, 0x18, 0x14, 0x26, 0xef, 0x54, 0xa5, 0xa8, 0x65, 0xa3,
+        0x9c, 0x81, 0xfa, 0x56, 0x8c, 0x2d, 0xce, 0x68, 0x40, 0xcb, 0xf1,
+        0x37, 0xbd, 0x5e, 0x85, 0xea, 0xd1, 0x0c, 0xbb, 0x19, 0x56, 0x23,
+        0x20, 0x1f, 0xad, 0x5c, 0x42, 0x08, 0x03, 0xb5, 0x55, 0x91, 0x04,
+        0xc9, 0x80, 0x38, 0x00, 0x0a, 0x71, 0x34, 0x6c, 0x32, 0x27, 0xe9,
+        0x55, 0x25, 0x15, 0x2c, 0x68, 0xa3, 0x30, 0xeb, 0x54, 0xa5, 0x15,
+        0x0c, 0xd1, 0x00, 0xff, 0xd9};
+
+    /* package */ static final byte[] sPhotoByteArrayForComplicatedCase;
+
+    static {
+        final int length = sPhotoIntArrayForComplicatedCase.length;
+        sPhotoByteArrayForComplicatedCase = new byte[length];
+        for (int i = 0; i < length; i++) {
+            sPhotoByteArrayForComplicatedCase[i] = (byte)sPhotoIntArrayForComplicatedCase[i];
+        }
+    }
+
+    public void testV21SimpleCase1_Parsing() {
+        mVerifier.initForImportTest(V21, R.raw.v21_simple_1);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("N", "Ando;Roid;", Arrays.asList("Ando", "Roid", ""));
+    }
+
+    public void testV21SimpleCase1_Type_Generic() {
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, R.raw.v21_simple_1);
+        mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                        .put(StructuredName.FAMILY_NAME, "Ando")
+                        .put(StructuredName.GIVEN_NAME, "Roid")
+                        .put(StructuredName.DISPLAY_NAME, "Roid Ando");
+    }
+
+    public void testV21SimpleCase1_Type_Japanese() {
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_1);
+        mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                        .put(StructuredName.FAMILY_NAME, "Ando")
+                        .put(StructuredName.GIVEN_NAME, "Roid")
+                        // If name-related strings only contains printable Ascii,
+                        // the order is remained to be US's:
+                        // "Prefix Given Middle Family Suffix"
+                        .put(StructuredName.DISPLAY_NAME, "Roid Ando");
+    }
+
+    public void testV21SimpleCase2() {
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, R.raw.v21_simple_2);
+        mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                        .put(StructuredName.DISPLAY_NAME, "Ando Roid");
+    }
+
+    public void testV21SimpleCase3() {
+        mVerifier.initForImportTest(V21, R.raw.v21_simple_3);
+        mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                        .put(StructuredName.FAMILY_NAME, "Ando")
+                        .put(StructuredName.GIVEN_NAME, "Roid")
+                        // "FN" field should be prefered since it should contain the original
+                        // order intended by the author of the file.
+                        .put(StructuredName.DISPLAY_NAME, "Ando Roid");
+    }
+
+    /**
+     * Tests ';' is properly handled by VCardParser implementation.
+     */
+    public void testV21BackslashCase_Parsing() {
+        mVerifier.initForImportTest(V21, R.raw.v21_backslash);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1")
+                .addExpectedNodeWithOrder("N", ";A;B\\;C\\;;D;:E;\\\\;",
+                        Arrays.asList("", "A;B\\", "C\\;", "D", ":E", "\\\\", ""))
+                .addExpectedNodeWithOrder("FN", "A;B\\C\\;D:E\\\\");
+        
+    }
+
+    /**
+     * Tests ContactStruct correctly ignores redundant fields in "N" property values and
+     * inserts name related data.
+     */
+    public void testV21BackslashCase() {
+        mVerifier.initForImportTest(V21, R.raw.v21_backslash);
+        mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                        // FAMILY_NAME is empty and removed in this test...
+                        .put(StructuredName.GIVEN_NAME, "A;B\\")
+                        .put(StructuredName.MIDDLE_NAME, "C\\;")
+                        .put(StructuredName.PREFIX, "D")
+                        .put(StructuredName.SUFFIX, ":E")
+                        .put(StructuredName.DISPLAY_NAME, "A;B\\C\\;D:E\\\\");
+    }
+
+    public void testOrgBeforTitle() {
+        mVerifier.initForImportTest(V21, R.raw.v21_org_before_title);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.DISPLAY_NAME, "Normal Guy");
+        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "Company")
+                .put(Organization.DEPARTMENT, "Organization Devision Room Sheet No.")
+                .put(Organization.TITLE, "Excellent Janitor")
+                .put(Organization.TYPE, Organization.TYPE_WORK);
+    }
+
+    public void testTitleBeforOrg() {
+        mVerifier.initForImportTest(V21, R.raw.v21_title_before_org);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.DISPLAY_NAME, "Nice Guy");
+        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "Marverous")
+                .put(Organization.DEPARTMENT, "Perfect Great Good Bad Poor")
+                .put(Organization.TITLE, "Cool Title")
+                .put(Organization.TYPE, Organization.TYPE_WORK);
+    }
+
+    /**
+     * Verifies that vCard importer correctly interpret "PREF" attribute to IS_PRIMARY.
+     * The data contain three cases: one "PREF", no "PREF" and multiple "PREF", in each type.
+     */
+    public void testV21PrefToIsPrimary() {
+        mVerifier.initForImportTest(V21, R.raw.v21_pref_handling);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.DISPLAY_NAME, "Smith");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "1")
+                .put(Phone.TYPE, Phone.TYPE_HOME);
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "2")
+                .put(Phone.TYPE, Phone.TYPE_WORK)
+                .put(Phone.IS_PRIMARY, 1);
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "3")
+                .put(Phone.TYPE, Phone.TYPE_ISDN);
+        elem.addExpected(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "test@example.com")
+                .put(Email.TYPE, Email.TYPE_HOME)
+                .put(Email.IS_PRIMARY, 1);
+        elem.addExpected(Email.CONTENT_ITEM_TYPE)
+                .put(Email.DATA, "test2@examination.com")
+                .put(Email.TYPE, Email.TYPE_MOBILE)
+                .put(Email.IS_PRIMARY, 1);
+        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "Company")
+                .put(Organization.TITLE, "Engineer")
+                .put(Organization.TYPE, Organization.TYPE_WORK);
+        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "Mystery")
+                .put(Organization.TITLE, "Blogger")
+                .put(Organization.TYPE, Organization.TYPE_WORK);
+        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "Poetry")
+                .put(Organization.TITLE, "Poet")
+                .put(Organization.TYPE, Organization.TYPE_WORK);
+    }
+
+    /**
+     * Tests all the properties in a complicated vCard are correctly parsed by the VCardParser.
+     */
+    public void testV21ComplicatedCase_Parsing() {
+        mVerifier.initForImportTest(V21, R.raw.v21_complicated);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1")
+                .addExpectedNodeWithOrder("N", "Gump;Forrest;Hoge;Pos;Tao",
+                        Arrays.asList("Gump", "Forrest", "Hoge", "Pos", "Tao"))
+                .addExpectedNodeWithOrder("FN", "Joe Due")
+                .addExpectedNodeWithOrder("ORG", "Gump Shrimp Co.;Sales Dept.;Manager;Fish keeper",
+                        Arrays.asList("Gump Shrimp Co.", "Sales Dept.;Manager", "Fish keeper"))
+                .addExpectedNodeWithOrder("ROLE", "Fish Cake Keeper!")
+                .addExpectedNodeWithOrder("TITLE", "Shrimp Man")
+                .addExpectedNodeWithOrder("X-CLASS", "PUBLIC")
+                .addExpectedNodeWithOrder("TEL", "(111) 555-1212", new TypeSet("WORK", "VOICE"))
+                .addExpectedNodeWithOrder("TEL", "(404) 555-1212", new TypeSet("HOME", "VOICE"))
+                .addExpectedNodeWithOrder("TEL", "0311111111", new TypeSet("CELL"))
+                .addExpectedNodeWithOrder("TEL", "0322222222", new TypeSet("VIDEO"))
+                .addExpectedNodeWithOrder("TEL", "0333333333", new TypeSet("VOICE"))
+                .addExpectedNodeWithOrder("ADR",
+                        ";;100 Waters Edge;Baytown;LA;30314;United States of America",
+                        Arrays.asList("", "", "100 Waters Edge", "Baytown",
+                                "LA", "30314", "United States of America"),
+                                null, null, new TypeSet("WORK"), null)
+                .addExpectedNodeWithOrder("LABEL",
+                        "100 Waters Edge\r\nBaytown, LA 30314\r\nUnited  States of America",
+                        null, null, mContentValuesForQP, new TypeSet("WORK"), null)
+                .addExpectedNodeWithOrder("ADR",
+                        ";;42 Plantation St.;Baytown;LA;30314;United States of America",
+                        Arrays.asList("", "", "42 Plantation St.", "Baytown",
+                                "LA", "30314", "United States of America"), null, null,
+                                new TypeSet("HOME"), null)
+                .addExpectedNodeWithOrder("LABEL",
+                        "42 Plantation St.\r\nBaytown, LA 30314\r\nUnited  States of America",
+                        null, null, mContentValuesForQP,
+                        new TypeSet("HOME"), null)
+                .addExpectedNodeWithOrder("EMAIL", "forrestgump@walladalla.com",
+                        new TypeSet("PREF", "INTERNET"))
+                .addExpectedNodeWithOrder("EMAIL", "cell@example.com", new TypeSet("CELL"))
+                .addExpectedNodeWithOrder("NOTE", "The following note is the example from RFC 2045.")
+                .addExpectedNodeWithOrder("NOTE",
+                        "Now's the time for all folk to come to the aid of their country.",
+                        null, null, mContentValuesForQP, null, null)
+                .addExpectedNodeWithOrder("PHOTO", null,
+                        null, sPhotoByteArrayForComplicatedCase, mContentValuesForBase64V21,
+                        new TypeSet("JPEG"), null)
+                .addExpectedNodeWithOrder("X-ATTRIBUTE", "Some String")
+                .addExpectedNodeWithOrder("BDAY", "19800101")
+                .addExpectedNodeWithOrder("GEO", "35.6563854,139.6994233")
+                .addExpectedNodeWithOrder("URL", "http://www.example.com/")
+                .addExpectedNodeWithOrder("REV", "20080424T195243Z");
+    }
+
+    /**
+     * Checks ContactStruct correctly inserts values in a complicated vCard
+     * into ContentResolver.
+     */
+    public void testV21ComplicatedCase() {
+        mVerifier.initForImportTest(V21, R.raw.v21_complicated);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "Gump")
+                .put(StructuredName.GIVEN_NAME, "Forrest")
+                .put(StructuredName.MIDDLE_NAME, "Hoge")
+                .put(StructuredName.PREFIX, "Pos")
+                .put(StructuredName.SUFFIX, "Tao")
+                .put(StructuredName.DISPLAY_NAME, "Joe Due");
+        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.TYPE, Organization.TYPE_WORK)
+                .put(Organization.COMPANY, "Gump Shrimp Co.")
+                .put(Organization.DEPARTMENT, "Sales Dept.;Manager Fish keeper")
+                .put(Organization.TITLE, "Shrimp Man");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_WORK)
+                // Phone number is expected to be formated with NAMP format in default.
+                .put(Phone.NUMBER, "111-555-1212");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_HOME)
+                .put(Phone.NUMBER, "404-555-1212");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_MOBILE)
+                .put(Phone.NUMBER, "031-111-1111");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "VIDEO")
+                .put(Phone.NUMBER, "032-222-2222");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "VOICE")
+                .put(Phone.NUMBER, "033-333-3333");
+        elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+                .put(StructuredPostal.COUNTRY, "United States of America")
+                .put(StructuredPostal.POSTCODE, "30314")
+                .put(StructuredPostal.REGION, "LA")
+                .put(StructuredPostal.CITY, "Baytown")
+                .put(StructuredPostal.STREET, "100 Waters Edge")
+                .put(StructuredPostal.FORMATTED_ADDRESS,
+                        "100 Waters Edge Baytown LA 30314 United States of America");
+        elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
+                .put(StructuredPostal.COUNTRY, "United States of America")
+                .put(StructuredPostal.POSTCODE, "30314")
+                .put(StructuredPostal.REGION, "LA")
+                .put(StructuredPostal.CITY, "Baytown")
+                .put(StructuredPostal.STREET, "42 Plantation St.")
+                .put(StructuredPostal.FORMATTED_ADDRESS,
+                        "42 Plantation St. Baytown LA 30314 United States of America");
+        elem.addExpected(Email.CONTENT_ITEM_TYPE)
+                // "TYPE=INTERNET" -> TYPE_CUSTOM + the label "INTERNET"
+                .put(Email.TYPE, Email.TYPE_CUSTOM)
+                .put(Email.LABEL, "INTERNET")
+                .put(Email.DATA, "forrestgump@walladalla.com")
+                .put(Email.IS_PRIMARY, 1);
+        elem.addExpected(Email.CONTENT_ITEM_TYPE)
+                .put(Email.TYPE, Email.TYPE_MOBILE)
+                .put(Email.DATA, "cell@example.com");
+        elem.addExpected(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "The following note is the example from RFC 2045.");
+        elem.addExpected(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE,
+                        "Now's the time for all folk to come to the aid of their country.");
+        elem.addExpected(Photo.CONTENT_ITEM_TYPE)
+                // No information about its image format can be inserted.
+                .put(Photo.PHOTO, sPhotoByteArrayForComplicatedCase);
+        elem.addExpected(Event.CONTENT_ITEM_TYPE)
+                .put(Event.START_DATE, "19800101")
+                .put(Event.TYPE, Event.TYPE_BIRTHDAY);
+        elem.addExpected(Website.CONTENT_ITEM_TYPE)
+                .put(Website.URL, "http://www.example.com/")
+                .put(Website.TYPE, Website.TYPE_HOMEPAGE);
+    }
+
+    public void testV30Simple_Parsing() {
+        mVerifier.initForImportTest(V30, R.raw.v30_simple);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "3.0")
+                .addExpectedNodeWithOrder("FN", "And Roid")
+                .addExpectedNodeWithOrder("N", "And;Roid;;;", Arrays.asList("And", "Roid", "", "", ""))
+                .addExpectedNodeWithOrder("ORG", "Open;Handset; Alliance",
+                        Arrays.asList("Open", "Handset", " Alliance"))
+                .addExpectedNodeWithOrder("SORT-STRING", "android")
+                .addExpectedNodeWithOrder("TEL", "0300000000", new TypeSet("PREF", "VOICE"))
+                .addExpectedNodeWithOrder("CLASS", "PUBLIC")
+                .addExpectedNodeWithOrder("X-GNO", "0")
+                .addExpectedNodeWithOrder("X-GN", "group0")
+                .addExpectedNodeWithOrder("X-REDUCTION", "0")
+                .addExpectedNodeWithOrder("REV", "20081031T065854Z");
+    }
+
+    public void testV30Simple() {
+        mVerifier.initForImportTest(V30, R.raw.v30_simple);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "And")
+                .put(StructuredName.GIVEN_NAME, "Roid")
+                .put(StructuredName.DISPLAY_NAME, "And Roid")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "android");
+        elem.addExpected(Organization.CONTENT_ITEM_TYPE)
+                .put(Organization.COMPANY, "Open")
+                .put(Organization.DEPARTMENT, "Handset  Alliance")
+                .put(Organization.TYPE, Organization.TYPE_WORK);
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "VOICE")
+                .put(Phone.NUMBER, "030-000-0000")
+                .put(Phone.IS_PRIMARY, 1);
+    }
+
+    public void testV21Japanese1_Parsing() {
+        // Though Japanese careers append ";;;;" at the end of the value of "SOUND",
+        // vCard 2.1/3.0 specification does not allow multiple values.
+        // Do not need to handle it as multiple values.
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
+                R.raw.v21_japanese_1);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1", null, null, null, null, null)
+                .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9;;;;",
+                        Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9", "", "", "", ""),
+                        null, mContentValuesForSJis, null, null)
+                .addExpectedNodeWithOrder("SOUND",
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E;;;;",
+                        null, null, mContentValuesForSJis,
+                        new TypeSet("X-IRMC-N"), null)
+                .addExpectedNodeWithOrder("TEL", "0300000000", null, null, null,
+                        new TypeSet("VOICE", "PREF"), null);
+    }
+
+    private void testV21Japanese1Common(int resId, int vcardType, boolean japanese) {
+        mVerifier.initForImportTest(vcardType, resId);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9")
+                .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9")
+                // While vCard parser does not split "SOUND" property values,
+                // ContactStruct care it.
+                .put(StructuredName.PHONETIC_GIVEN_NAME,
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                // Phone number formatting is different.
+                .put(Phone.NUMBER, (japanese ? "03-0000-0000" : "030-000-0000"))
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "VOICE")
+                .put(Phone.IS_PRIMARY, 1);
+    }
+
+    /**
+     * Verifies vCard with Japanese can be parsed correctly with
+     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_GENERIC_UTF8}.
+     */
+    public void testV21Japanese1_Type_Generic_Utf8() {
+        testV21Japanese1Common(
+                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8, false);
+    }
+
+    /**
+     * Verifies vCard with Japanese can be parsed correctly with
+     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_SJIS}.
+     */
+    public void testV21Japanese1_Type_Japanese_Sjis() {
+        testV21Japanese1Common(
+                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS, true);
+    }
+
+    /**
+     * Verifies vCard with Japanese can be parsed correctly with
+     * {@link android.pim.vcard.VCardConfig#VCARD_TYPE_V21_JAPANESE_UTF8}.
+     * since vCard 2.1 specifies the charset of each line if it contains non-Ascii.
+     */
+    public void testV21Japanese1_Type_Japanese_Utf8() {
+        testV21Japanese1Common(
+                R.raw.v21_japanese_1, VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8, true);
+    }
+
+    public void testV21Japanese2_Parsing() {
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
+                R.raw.v21_japanese_2);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1")
+                .addExpectedNodeWithOrder("N", "\u5B89\u85E4;\u30ED\u30A4\u30C9\u0031;;;",
+                        Arrays.asList("\u5B89\u85E4", "\u30ED\u30A4\u30C9\u0031",
+                                "", "", ""),
+                        null, mContentValuesForSJis, null, null)
+                .addExpectedNodeWithOrder("FN", "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031",
+                        null, null, mContentValuesForSJis, null, null)
+                .addExpectedNodeWithOrder("SOUND",
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73;\uFF9B\uFF72\uFF84\uFF9E\u0031;;;",
+                        null, null, mContentValuesForSJis,
+                        new TypeSet("X-IRMC-N"), null)
+                .addExpectedNodeWithOrder("ADR",
+                        ";\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+                        "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+                        "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC\u0036" +
+                        "\u968E;;;;150-8512;",
+                        Arrays.asList("",
+                                "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+                                "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+                                "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+                                "\u0036\u968E", "", "", "", "150-8512", ""),
+                        null, mContentValuesForQPAndSJis, new TypeSet("HOME"), null)
+                .addExpectedNodeWithOrder("NOTE", "\u30E1\u30E2", null, null,
+                        mContentValuesForQPAndSJis, null, null);
+    }
+
+    public void testV21Japanese2_Type_Generic_Utf8() {
+        mVerifier.initForImportTest(V21, R.raw.v21_japanese_2);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4")
+                .put(StructuredName.GIVEN_NAME, "\u30ED\u30A4\u30C9\u0031")
+                .put(StructuredName.DISPLAY_NAME,
+                        "\u5B89\u85E4\u0020\u30ED\u30A4\u30C9\u0020\u0031")
+                // ContactStruct should correctly split "SOUND" property into several elements,
+                // even though VCardParser side does not care it.
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF71\uFF9D\uFF84\uFF9E\uFF73")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF9B\uFF72\uFF84\uFF9E\u0031");
+        elem.addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.POSTCODE, "150-8512")
+                .put(StructuredPostal.STREET,
+                        "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+                        "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+                        "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+                        "\u0036\u968E")
+                .put(StructuredPostal.FORMATTED_ADDRESS,
+                        "\u6771\u4EAC\u90FD\u6E0B\u8C37\u533A\u685C" +
+                        "\u4E18\u753A\u0032\u0036\u002D\u0031\u30BB" +
+                        "\u30EB\u30EA\u30A2\u30F3\u30BF\u30EF\u30FC" +
+                        "\u0036\u968E 150-8512")
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
+        elem.addExpected(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "\u30E1\u30E2");
+    }
+
+    public void testV21MultipleEntryCase_Parse() {
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
+                R.raw.v21_multiple_entry);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1")
+                .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033;;;;",
+                        Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0033", "", "", "", ""),
+                        null, mContentValuesForSJis, null, null)
+                .addExpectedNodeWithOrder("SOUND",
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033;;;;",
+                        null, null, mContentValuesForSJis,
+                        new TypeSet("X-IRMC-N"), null)
+                .addExpectedNodeWithOrder("TEL", "9", new TypeSet("X-NEC-SECRET"))
+                .addExpectedNodeWithOrder("TEL", "10", new TypeSet("X-NEC-HOTEL"))
+                .addExpectedNodeWithOrder("TEL", "11", new TypeSet("X-NEC-SCHOOL"))
+                .addExpectedNodeWithOrder("TEL", "12", new TypeSet("FAX", "HOME"));
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1")
+                .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034;;;;",
+                        Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0034", "", "", "", ""),
+                        null, mContentValuesForSJis, null, null)
+                .addExpectedNodeWithOrder("SOUND",
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034;;;;",
+                        null, null, mContentValuesForSJis,
+                        new TypeSet("X-IRMC-N"), null)
+                .addExpectedNodeWithOrder("TEL", "13", new TypeSet("MODEM"))
+                .addExpectedNodeWithOrder("TEL", "14", new TypeSet("PAGER"))
+                .addExpectedNodeWithOrder("TEL", "15", new TypeSet("X-NEC-FAMILY"))
+                .addExpectedNodeWithOrder("TEL", "16", new TypeSet("X-NEC-GIRL"));
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1")
+                .addExpectedNodeWithOrder("N", "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035;;;;",
+                        Arrays.asList("\u5B89\u85E4\u30ED\u30A4\u30C9\u0035", "", "", "", ""),
+                        null, mContentValuesForSJis, null, null)
+                .addExpectedNodeWithOrder("SOUND",
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035;;;;",
+                        null, null, mContentValuesForSJis,
+                        new TypeSet("X-IRMC-N"), null)
+                .addExpectedNodeWithOrder("TEL", "17", new TypeSet("X-NEC-BOY"))
+                .addExpectedNodeWithOrder("TEL", "18", new TypeSet("X-NEC-FRIEND"))
+                .addExpectedNodeWithOrder("TEL", "19", new TypeSet("X-NEC-PHS"))
+                .addExpectedNodeWithOrder("TEL", "20", new TypeSet("X-NEC-RESTAURANT"));
+    }
+
+    public void testV21MultipleEntryCase() {
+        mVerifier.initForImportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS,
+                R.raw.v21_multiple_entry);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033")
+                .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0033")
+                .put(StructuredName.PHONETIC_GIVEN_NAME,
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0033");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-SECRET")
+                .put(Phone.NUMBER, "9");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-HOTEL")
+                .put(Phone.NUMBER, "10");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-SCHOOL")
+                .put(Phone.NUMBER, "11");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_FAX_HOME)
+                .put(Phone.NUMBER, "12");
+
+        elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034")
+                .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0034")
+                .put(StructuredName.PHONETIC_GIVEN_NAME,
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0034");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "MODEM")
+                .put(Phone.NUMBER, "13");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_PAGER)
+                .put(Phone.NUMBER, "14");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-FAMILY")
+                .put(Phone.NUMBER, "15");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-GIRL")
+                .put(Phone.NUMBER, "16");
+
+        elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035")
+                .put(StructuredName.DISPLAY_NAME, "\u5B89\u85E4\u30ED\u30A4\u30C9\u0035")
+                .put(StructuredName.PHONETIC_GIVEN_NAME,
+                        "\uFF71\uFF9D\uFF84\uFF9E\uFF73\uFF9B\uFF72\uFF84\uFF9E\u0035");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-BOY")
+                .put(Phone.NUMBER, "17");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-FRIEND")
+                .put(Phone.NUMBER, "18");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-PHS")
+                .put(Phone.NUMBER, "19");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_CUSTOM)
+                .put(Phone.LABEL, "NEC-RESTAURANT")
+                .put(Phone.NUMBER, "20");
+    }
+
+    public void testIgnoreAgentV21_Parse() {
+        mVerifier.initForImportTest(V21, R.raw.v21_winmo_65);
+        ContentValues contentValuesForValue = new ContentValues();
+        contentValuesForValue.put("VALUE", "DATE");
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "2.1")
+                .addExpectedNodeWithOrder("N", Arrays.asList("Example", "", "", "", ""))
+                .addExpectedNodeWithOrder("FN", "Example")
+                .addExpectedNodeWithOrder("ANNIVERSARY", "20091010", contentValuesForValue)
+                .addExpectedNodeWithOrder("AGENT", "")
+                .addExpectedNodeWithOrder("X-CLASS", "PUBLIC")
+                .addExpectedNodeWithOrder("X-REDUCTION", "")
+                .addExpectedNodeWithOrder("X-NO", "");
+    }
+
+    public void testIgnoreAgentV21() {
+        mVerifier.initForImportTest(V21, R.raw.v21_winmo_65);
+        ContentValuesVerifier verifier = new ContentValuesVerifier();
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "Example")
+                .put(StructuredName.DISPLAY_NAME, "Example");
+    }
+
+    public void testTolerateInvalidCommentLikeLineV21() {
+        mVerifier.initForImportTest(V21, R.raw.v21_invalid_comment_line);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.GIVEN_NAME, "Conference Call")
+                .put(StructuredName.DISPLAY_NAME, "Conference Call");
+        elem.addExpected(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "This is an (sharp ->#<- sharp) example. "
+                        + "This message must NOT be ignored.");
+    }
+
+    public void testPagerV30_Parse() {
+        mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNodeWithOrder("VERSION", "3.0")
+                .addExpectedNodeWithOrder("N", Arrays.asList("F", "G", "M", "", ""))
+                .addExpectedNodeWithOrder("TEL", "6101231234@pagersample.com",
+                        new TypeSet("WORK", "MSG", "PAGER"));
+    }
+
+    public void testPagerV30() {
+        mVerifier.initForImportTest(V30, R.raw.v30_comma_separated);
+        ContentValuesVerifierElem elem = mVerifier.addContentValuesVerifierElem();
+        elem.addExpected(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "F")
+                .put(StructuredName.MIDDLE_NAME, "M")
+                .put(StructuredName.GIVEN_NAME, "G")
+                .put(StructuredName.DISPLAY_NAME, "G M F");
+        elem.addExpected(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.TYPE, Phone.TYPE_PAGER)
+                .put(Phone.NUMBER, "6101231234@pagersample.com");
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
new file mode 100644
index 0000000..5b60342
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardJapanizationTests.java
@@ -0,0 +1,434 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardConfig;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+
+import android.pim.vcard.PropertyNodesVerifierElem.TypeSet;
+
+import java.util.Arrays;
+
+public class VCardJapanizationTests extends VCardTestsBase {
+    private void testNameUtf8Common(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+                .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+                .put(StructuredName.MIDDLE_NAME, "B")
+                .put(StructuredName.PREFIX, "Dr.")
+                .put(StructuredName.SUFFIX, "Ph.D");
+        ContentValues contentValues =
+            (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8);
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
+                        contentValues)
+                .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
+                        Arrays.asList(
+                                "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
+                                null, contentValues, null, null);
+    }
+
+    public void testNameUtf8V21() {
+        testNameUtf8Common(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+    }
+
+    public void testNameUtf8V30() {
+        testNameUtf8Common(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+    }
+
+    public void testNameShiftJis() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+                .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+                .put(StructuredName.MIDDLE_NAME, "B")
+                .put(StructuredName.PREFIX, "Dr.")
+                .put(StructuredName.SUFFIX, "Ph.D");
+
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("FN", "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D",
+                        mContentValuesForSJis)
+                .addExpectedNode("N", "\u3075\u308B\u3069;\u3091\u308A\u304B;B;Dr.;Ph.D",
+                        Arrays.asList(
+                                "\u3075\u308B\u3069", "\u3091\u308A\u304B", "B", "Dr.", "Ph.D"),
+                                null, mContentValuesForSJis, null, null);
+    }
+
+    /**
+     * DoCoMo phones require all name elements should be in "family name" field.
+     */
+    public void testNameDoCoMo() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.FAMILY_NAME, "\u3075\u308B\u3069")
+                .put(StructuredName.GIVEN_NAME, "\u3091\u308A\u304B")
+                .put(StructuredName.MIDDLE_NAME, "B")
+                .put(StructuredName.PREFIX, "Dr.")
+                .put(StructuredName.SUFFIX, "Ph.D");
+
+        final String fullName = "Dr. \u3075\u308B\u3069 B \u3091\u308A\u304B Ph.D";
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("N", fullName + ";;;;",
+                        Arrays.asList(fullName, "", "", "", ""),
+                        null, mContentValuesForSJis, null, null)
+                .addExpectedNode("FN", fullName, mContentValuesForSJis)
+                .addExpectedNode("SOUND", ";;;;", new TypeSet("X-IRMC-N"))
+                .addExpectedNode("TEL", "", new TypeSet("HOME"))
+                .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+                .addExpectedNode("ADR", "", new TypeSet("HOME"))
+                .addExpectedNode("X-CLASS", "PUBLIC")
+                .addExpectedNode("X-REDUCTION", "")
+                .addExpectedNode("X-NO", "")
+                .addExpectedNode("X-DCM-HMN-MODE", "");
+    }
+
+    private void testPhoneticNameCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
+
+        final ContentValues contentValues =
+            (VCardConfig.usesShiftJis(vcardType) ?
+                    (VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
+                            mContentValuesForQPAndSJis) :
+                    (VCardConfig.isV30(vcardType) ? null : mContentValuesForQPAndUtf8));
+        PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
+        elem.addExpectedNode("X-PHONETIC-LAST-NAME", "\u3084\u307E\u3060",
+                        contentValues)
+                .addExpectedNode("X-PHONETIC-MIDDLE-NAME",
+                        "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0",
+                        contentValues)
+                .addExpectedNode("X-PHONETIC-FIRST-NAME", "\u305F\u308D\u3046",
+                        contentValues);
+        if (VCardConfig.isV30(vcardType)) {
+            elem.addExpectedNode("SORT-STRING",
+                    "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 \u305F\u308D\u3046",
+                    contentValues);
+        }
+        ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE);
+        builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046")
+                .put(StructuredName.DISPLAY_NAME,
+                        "\u3084\u307E\u3060 \u30DF\u30C9\u30EB\u30CD\u30FC\u30E0 " +
+                        "\u305F\u308D\u3046");
+    }
+
+    public void testPhoneticNameForJapaneseV21Utf8() {
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+    }
+
+    public void testPhoneticNameForJapaneseV21Sjis() {
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+    }
+
+    public void testPhoneticNameForJapaneseV30Utf8() {
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+    }
+
+    public void testPhoneticNameForJapaneseV30SJis() {
+        testPhoneticNameCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_SJIS);
+    }
+
+    public void testPhoneticNameForMobileV21_1() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME, "\u30DF\u30C9\u30EB\u30CD\u30FC\u30E0")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
+
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("SOUND",
+                        "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " +
+                        "\uFF80\uFF9B\uFF73;;;;",
+                        mContentValuesForSJis, new TypeSet("X-IRMC-N"));
+        ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE);
+        builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E")
+                .put(StructuredName.PHONETIC_MIDDLE_NAME,
+                        "\uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73")
+                .put(StructuredName.DISPLAY_NAME,
+                        "\uFF94\uFF8F\uFF80\uFF9E \uFF90\uFF84\uFF9E\uFF99\uFF88\uFF70\uFF91 " +
+                        "\uFF80\uFF9B\uFF73");
+    }
+
+    public void testPhoneticNameForMobileV21_2() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_JAPANESE_MOBILE);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredName.CONTENT_ITEM_TYPE)
+                .put(StructuredName.PHONETIC_FAMILY_NAME, "\u3084\u307E\u3060")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "\u305F\u308D\u3046");
+
+        mVerifier.addPropertyNodesVerifierElem()
+                .addExpectedNode("SOUND", "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73;;;;",
+                                mContentValuesForSJis, new TypeSet("X-IRMC-N"));
+        ContentValuesBuilder builder = mVerifier.addContentValuesVerifierElem()
+                .addExpected(StructuredName.CONTENT_ITEM_TYPE);
+        builder.put(StructuredName.PHONETIC_FAMILY_NAME, "\uFF94\uFF8F\uFF80\uFF9E")
+                .put(StructuredName.PHONETIC_GIVEN_NAME, "\uFF80\uFF9B\uFF73")
+                .put(StructuredName.DISPLAY_NAME, "\uFF94\uFF8F\uFF80\uFF9E \uFF80\uFF9B\uFF73");
+    }
+
+    private void testPostalAddressWithJapaneseCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
+                .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751")
+                .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02")
+                .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C")
+                .put(StructuredPostal.POSTCODE, "494-1313")
+                .put(StructuredPostal.COUNTRY, "\u65E5\u672C")
+                .put(StructuredPostal.FORMATTED_ADDRESS,
+                        "\u3053\u3093\u306A\u3068\u3053\u308D\u3092\u898B"
+                        + "\u308B\u306A\u3093\u3066\u6687\u4EBA\u3067\u3059\u304B\uFF1F")
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+                .put(StructuredPostal.LABEL, "\u304A\u3082\u3061\u304B\u3048\u308A");
+
+        ContentValues contentValues = (VCardConfig.usesShiftJis(vcardType) ?
+                (VCardConfig.isV30(vcardType) ? mContentValuesForSJis :
+                    mContentValuesForQPAndSJis) :
+                (VCardConfig.isV30(vcardType) ? mContentValuesForUtf8 :
+                    mContentValuesForQPAndUtf8));
+
+        PropertyNodesVerifierElem elem = mVerifier.addPropertyNodesVerifierElemWithEmptyName();
+        // LABEL must be ignored in vCard 2.1. As for vCard 3.0, the current behavior is
+        // same as that in vCard 3.0, which can be changed in the future.
+        elem.addExpectedNode("ADR", Arrays.asList("\u79C1\u66F8\u7BB107",
+                "", "\u96DB\u898B\u6CA2\u6751", "\u9E7F\u9AA8\u5E02", "\u00D7\u00D7\u770C",
+                "494-1313", "\u65E5\u672C"),
+                contentValues);
+        mVerifier.addContentValuesVerifierElem().addExpected(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.POBOX, "\u79C1\u66F8\u7BB107")
+                .put(StructuredPostal.STREET, "\u96DB\u898B\u6CA2\u6751")
+                .put(StructuredPostal.CITY, "\u9E7F\u9AA8\u5E02")
+                .put(StructuredPostal.REGION, "\u00D7\u00D7\u770C")
+                .put(StructuredPostal.POSTCODE, "494-1313")
+                .put(StructuredPostal.COUNTRY, "\u65E5\u672C")
+                .put(StructuredPostal.FORMATTED_ADDRESS,
+                        "\u65E5\u672C 494-1313 \u00D7\u00D7\u770C \u9E7F\u9AA8\u5E02 " +
+                        "\u96DB\u898B\u6CA2\u6751 " + "\u79C1\u66F8\u7BB107")
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME);
+    }
+    public void testPostalAddresswithJapaneseV21() {
+        testPostalAddressWithJapaneseCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_SJIS);
+    }
+
+    /**
+     * Verifies that only one address field is emitted toward DoCoMo phones.
+     * Prefered type must (should?) be: HOME > WORK > OTHER > CUSTOM
+     */
+    public void testPostalAdrressForDoCoMo_1() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+                .put(StructuredPostal.POBOX, "1");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+                .put(StructuredPostal.POBOX, "2");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
+                .put(StructuredPostal.POBOX, "3");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+                .put(StructuredPostal.LABEL, "custom")
+                .put(StructuredPostal.POBOX, "4");
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "", new TypeSet("HOME"))
+                .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+                .addExpectedNode("X-CLASS", "PUBLIC")
+                .addExpectedNode("X-REDUCTION", "")
+                .addExpectedNode("X-NO", "")
+                .addExpectedNode("X-DCM-HMN-MODE", "")
+                .addExpectedNode("ADR",
+                        Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME"));
+    }
+
+    public void testPostalAdrressForDoCoMo_2() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+                .put(StructuredPostal.POBOX, "1");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+                .put(StructuredPostal.POBOX, "2");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+                .put(StructuredPostal.LABEL, "custom")
+                .put(StructuredPostal.POBOX, "3");
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "", new TypeSet("HOME"))
+                .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+                .addExpectedNode("X-CLASS", "PUBLIC")
+                .addExpectedNode("X-REDUCTION", "")
+                .addExpectedNode("X-NO", "")
+                .addExpectedNode("X-DCM-HMN-MODE", "")
+                .addExpectedNode("ADR",
+                        Arrays.asList("2", "", "", "", "", "", ""), new TypeSet("WORK"));
+    }
+
+    public void testPostalAdrressForDoCoMo_3() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+                .put(StructuredPostal.LABEL, "custom1")
+                .put(StructuredPostal.POBOX, "1");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+                .put(StructuredPostal.POBOX, "2");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_CUSTOM)
+                .put(StructuredPostal.LABEL, "custom2")
+                .put(StructuredPostal.POBOX, "3");
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "", new TypeSet("HOME"))
+                .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+                .addExpectedNode("X-CLASS", "PUBLIC")
+                .addExpectedNode("X-REDUCTION", "")
+                .addExpectedNode("X-NO", "")
+                .addExpectedNode("X-DCM-HMN-MODE", "")
+                .addExpectedNode("ADR", Arrays.asList("2", "", "", "", "", "", ""));
+    }
+
+    /**
+     * Verifies the vCard exporter tolerates null TYPE.
+     */
+    public void testPostalAdrressForDoCoMo_4() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.POBOX, "1");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_OTHER)
+                .put(StructuredPostal.POBOX, "2");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
+                .put(StructuredPostal.POBOX, "3");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.TYPE, StructuredPostal.TYPE_WORK)
+                .put(StructuredPostal.POBOX, "4");
+        entry.addContentValues(StructuredPostal.CONTENT_ITEM_TYPE)
+                .put(StructuredPostal.POBOX, "5");
+
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "", new TypeSet("HOME"))
+                .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+                .addExpectedNode("X-CLASS", "PUBLIC")
+                .addExpectedNode("X-REDUCTION", "")
+                .addExpectedNode("X-NO", "")
+                .addExpectedNode("X-DCM-HMN-MODE", "")
+                .addExpectedNode("ADR",
+                        Arrays.asList("3", "", "", "", "", "", ""), new TypeSet("HOME"));
+    }
+
+    private void testJapanesePhoneNumberCommon(int vcardType) {
+        mVerifier.initForExportTest(vcardType);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "0312341234")
+                .put(Phone.TYPE, Phone.TYPE_HOME);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "09012341234")
+                .put(Phone.TYPE, Phone.TYPE_MOBILE);
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME"))
+                .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL"));
+    }
+
+    public void testJapanesePhoneNumberV21_1() {
+        testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V21_JAPANESE_UTF8);
+    }
+
+    public void testJapanesePhoneNumberV30() {
+        testJapanesePhoneNumberCommon(VCardConfig.VCARD_TYPE_V30_JAPANESE_UTF8);
+    }
+
+    public void testJapanesePhoneNumberDoCoMo() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "0312341234")
+                .put(Phone.TYPE, Phone.TYPE_HOME);
+        entry.addContentValues(Phone.CONTENT_ITEM_TYPE)
+                .put(Phone.NUMBER, "09012341234")
+                .put(Phone.TYPE, Phone.TYPE_MOBILE);
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+                .addExpectedNode("X-CLASS", "PUBLIC")
+                .addExpectedNode("X-REDUCTION", "")
+                .addExpectedNode("X-NO", "")
+                .addExpectedNode("X-DCM-HMN-MODE", "")
+                .addExpectedNode("ADR", "", new TypeSet("HOME"))
+                .addExpectedNode("TEL", "03-1234-1234", new TypeSet("HOME"))
+                .addExpectedNode("TEL", "090-1234-1234", new TypeSet("CELL"));
+    }
+
+    public void testNoteDoCoMo() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_DOCOMO);
+        ContactEntry entry = mVerifier.addInputEntry();
+        entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "note1");
+        entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "note2");
+        entry.addContentValues(Note.CONTENT_ITEM_TYPE)
+                .put(Note.NOTE, "note3");
+
+        // More than one note fields must be aggregated into one note.
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("TEL", "", new TypeSet("HOME"))
+                .addExpectedNode("EMAIL", "", new TypeSet("HOME"))
+                .addExpectedNode("X-CLASS", "PUBLIC")
+                .addExpectedNode("X-REDUCTION", "")
+                .addExpectedNode("X-NO", "")
+                .addExpectedNode("X-DCM-HMN-MODE", "")
+                .addExpectedNode("ADR", "", new TypeSet("HOME"))
+                .addExpectedNode("NOTE", "note1\nnote2\nnote3", mContentValuesForQP);
+    }
+
+    public void testAndroidCustomV21() {
+        mVerifier.initForExportTest(VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8);
+        mVerifier.addInputEntry().addContentValues(Nickname.CONTENT_ITEM_TYPE)
+                .put(Nickname.NAME, "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC");
+        mVerifier.addPropertyNodesVerifierElemWithEmptyName()
+                .addExpectedNode("X-ANDROID-CUSTOM",
+                        Arrays.asList(Nickname.CONTENT_ITEM_TYPE,
+                                "\u304D\u3083\u30FC\u30A8\u30C3\u30C1\u30FC",
+                                "", "", "", "", "", "", "", "", "", "", "", "", "", ""),
+                        mContentValuesForQPAndUtf8);
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
new file mode 100644
index 0000000..0857e0c
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardTestsBase.java
@@ -0,0 +1,169 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentValues;
+import android.content.EntityIterator;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.database.IBulkCursor;
+import android.database.IContentObserver;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.pim.vcard.VCardConfig;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Almost a dead copy of android.test.mock.MockContentProvider, but different in that this
+ * class extends ContentProvider, not implementing IContentProvider,
+ * so that MockContentResolver is able to accept this class :(
+ */
+class MockContentProvider extends ContentProvider {
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public int bulkInsert(Uri url, ContentValues[] initialValues) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @SuppressWarnings("unused")
+    public IBulkCursor bulkQuery(Uri url, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder, IContentObserver observer,
+            CursorWindow window) throws RemoteException {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    @SuppressWarnings("unused")
+    public int delete(Uri url, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public String getType(Uri url) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Uri insert(Uri url, ContentValues initialValues) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public ParcelFileDescriptor openFile(Uri url, String mode) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public AssetFileDescriptor openAssetFile(Uri uri, String mode) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    @Override
+    public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+
+    public IBinder asBinder() {
+        throw new UnsupportedOperationException("unimplemented mock method");
+    }
+}
+
+/**
+ * BaseClass for vCard unit tests with utility classes.
+ * Please do not add each unit test here.
+ */
+/* package */ class VCardTestsBase extends AndroidTestCase {
+    public static final int V21 = VCardConfig.VCARD_TYPE_V21_GENERIC_UTF8;
+    public static final int V30 = VCardConfig.VCARD_TYPE_V30_GENERIC_UTF8;
+
+    // Do not modify these during tests.
+    protected final ContentValues mContentValuesForQP;
+    protected final ContentValues mContentValuesForSJis;
+    protected final ContentValues mContentValuesForUtf8;
+    protected final ContentValues mContentValuesForQPAndSJis;
+    protected final ContentValues mContentValuesForQPAndUtf8;
+    protected final ContentValues mContentValuesForBase64V21;
+    protected final ContentValues mContentValuesForBase64V30;
+
+    protected VCardVerifier mVerifier;
+    private boolean mSkipVerification;
+
+    public VCardTestsBase() {
+        super();
+        mContentValuesForQP = new ContentValues();
+        mContentValuesForQP.put("ENCODING", "QUOTED-PRINTABLE");
+        mContentValuesForSJis = new ContentValues();
+        mContentValuesForSJis.put("CHARSET", "SHIFT_JIS");
+        mContentValuesForUtf8 = new ContentValues();
+        mContentValuesForUtf8.put("CHARSET", "UTF-8");
+        mContentValuesForQPAndSJis = new ContentValues();
+        mContentValuesForQPAndSJis.put("ENCODING", "QUOTED-PRINTABLE");
+        mContentValuesForQPAndSJis.put("CHARSET", "SHIFT_JIS");
+        mContentValuesForQPAndUtf8 = new ContentValues();
+        mContentValuesForQPAndUtf8.put("ENCODING", "QUOTED-PRINTABLE");
+        mContentValuesForQPAndUtf8.put("CHARSET", "UTF-8");
+        mContentValuesForBase64V21 = new ContentValues();
+        mContentValuesForBase64V21.put("ENCODING", "BASE64");
+        mContentValuesForBase64V30 = new ContentValues();
+        mContentValuesForBase64V30.put("ENCODING", "b");
+    }
+
+    @Override
+    public void testAndroidTestCaseSetupProperly() {
+        super.testAndroidTestCaseSetupProperly();
+        mSkipVerification = true;
+    }
+
+    @Override
+    public void setUp() throws Exception{
+        super.setUp();
+        mVerifier = new VCardVerifier(this);
+        mSkipVerification = false;
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        if (!mSkipVerification) {
+            mVerifier.verify();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
new file mode 100644
index 0000000..59299f9
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardUtilsTests.java
@@ -0,0 +1,85 @@
+/*
+ * 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.pim.vcard;
+
+import android.pim.vcard.VCardUtils;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+public class VCardUtilsTests extends TestCase {
+    public void testContainsOnlyPrintableAscii() {
+        assertTrue(VCardUtils.containsOnlyPrintableAscii((String)null));
+        assertTrue(VCardUtils.containsOnlyPrintableAscii((String[])null));
+        assertTrue(VCardUtils.containsOnlyPrintableAscii((List<String>)null));
+        assertTrue(VCardUtils.containsOnlyPrintableAscii(""));
+        assertTrue(VCardUtils.containsOnlyPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
+        assertTrue(VCardUtils.containsOnlyPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0x20; i < 0x7F; i++) {
+            builder.append((char)i);
+        }
+        assertTrue(VCardUtils.containsOnlyPrintableAscii(builder.toString()));
+        assertTrue(VCardUtils.containsOnlyPrintableAscii("\r\n"));
+        assertFalse(VCardUtils.containsOnlyPrintableAscii("\u0019"));
+        assertFalse(VCardUtils.containsOnlyPrintableAscii("\u007F"));
+    }
+
+    public void testContainsOnlyNonCrLfPrintableAscii() {
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String)null));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((String[])null));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii((List<String>)null));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(""));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0x20; i < 0x7F; i++) {
+            builder.append((char)i);
+        }
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii(builder.toString()));
+        assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u0019"));
+        assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\u007F"));
+        assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\r"));
+        assertFalse(VCardUtils.containsOnlyNonCrLfPrintableAscii("\n"));
+    }
+
+    public void testContainsOnlyAlphaDigitHyphen() {
+        assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String)null));
+        assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((String[])null));
+        assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen((List<String>)null));
+        assertTrue(VCardUtils.containsOnlyAlphaDigitHyphen(""));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("abcdefghijklmnopqrstuvwxyz"));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+        assertTrue(VCardUtils.containsOnlyNonCrLfPrintableAscii("0123456789-"));
+        for (int i = 0; i < 0x30; i++) {
+            if (i == 0x2D) {  // -
+                continue;
+            }
+            assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+        }
+        for (int i = 0x3A; i < 0x41; i++) {
+            assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+        }
+        for (int i = 0x5B; i < 0x61; i++) {
+            assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+        }
+        for (int i = 0x7B; i < 0x100; i++) {
+            assertFalse(VCardUtils.containsOnlyAlphaDigitHyphen(String.valueOf((char)i)));
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
new file mode 100644
index 0000000..bfc3158
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VCardVerifier.java
@@ -0,0 +1,306 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.EntityIterator;
+import android.net.Uri;
+import android.pim.vcard.VCardComposer;
+import android.pim.vcard.VCardConfig;
+import android.pim.vcard.VCardEntryConstructor;
+import android.pim.vcard.VCardInterpreter;
+import android.pim.vcard.VCardInterpreterCollection;
+import android.pim.vcard.VCardParser;
+import android.pim.vcard.VCardParser_V21;
+import android.pim.vcard.VCardParser_V30;
+import android.pim.vcard.exception.VCardException;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContext;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/* package */ class CustomMockContext extends MockContext {
+    final ContentResolver mResolver;
+    public CustomMockContext(ContentResolver resolver) {
+        mResolver = resolver;
+    }
+
+    @Override
+    public ContentResolver getContentResolver() {
+        return mResolver;
+    }
+}
+
+/* package */ class VCardVerifier {
+    private class VCardVerifierInternal implements VCardComposer.OneEntryHandler {
+        public boolean onInit(Context context) {
+            return true;
+        }
+        public boolean onEntryCreated(String vcard) {
+            verifyOneVCard(vcard);
+            return true;
+        }
+        public void onTerminate() {
+        }
+    }
+
+    private final AndroidTestCase mTestCase;
+    private final VCardVerifierInternal mVCardVerifierInternal;
+    private int mVCardType;
+    private boolean mIsV30;
+    private boolean mIsDoCoMo;
+
+    // Only one of them must be non-empty.
+    private ExportTestResolver mExportTestResolver;
+    private InputStream mInputStream;
+
+    // To allow duplication, use list instead of set.
+    // When null, we don't need to do the verification.
+    private PropertyNodesVerifier mPropertyNodesVerifier;
+    private LineVerifier mLineVerifier;
+    private ContentValuesVerifier mContentValuesVerifier;
+    private boolean mInitialized;
+    private boolean mVerified = false;
+
+    public VCardVerifier(AndroidTestCase androidTestCase) {
+        mTestCase = androidTestCase;
+        mVCardVerifierInternal = new VCardVerifierInternal();
+        mExportTestResolver = null;
+        mInputStream = null;
+        mInitialized = false;
+        mVerified = false;
+    }
+
+    public void initForExportTest(int vcardType) {
+        if (mInitialized) {
+            mTestCase.fail("Already initialized");
+        }
+        mExportTestResolver = new ExportTestResolver(mTestCase);
+        mVCardType = vcardType;
+        mIsV30 = VCardConfig.isV30(vcardType);
+        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
+        mInitialized = true;
+    }
+
+    public void initForImportTest(int vcardType, int resId) {
+        if (mInitialized) {
+            mTestCase.fail("Already initialized");
+        }
+        mVCardType = vcardType;
+        mIsV30 = VCardConfig.isV30(vcardType);
+        mIsDoCoMo = VCardConfig.isDoCoMo(vcardType);
+        setInputResourceId(resId);
+        mInitialized = true;
+    }
+
+    private void setInputResourceId(int resId) {
+        InputStream inputStream = mTestCase.getContext().getResources().openRawResource(resId);
+        if (inputStream == null) {
+            mTestCase.fail("Wrong resId: " + resId);
+        }
+        setInputStream(inputStream);
+    }
+
+    private void setInputStream(InputStream inputStream) {
+        if (mExportTestResolver != null) {
+            mTestCase.fail("addInputEntry() is called.");
+        } else if (mInputStream != null) {
+            mTestCase.fail("InputStream is already set");
+        }
+        mInputStream = inputStream;
+    }
+
+    public ContactEntry addInputEntry() {
+        if (!mInitialized) {
+            mTestCase.fail("Not initialized");
+        }
+        if (mInputStream != null) {
+            mTestCase.fail("setInputStream is called");
+        }
+        return mExportTestResolver.addInputContactEntry();
+    }
+
+    public PropertyNodesVerifierElem addPropertyNodesVerifierElem() {
+        if (!mInitialized) {
+            mTestCase.fail("Not initialized");
+        }
+        if (mPropertyNodesVerifier == null) {
+            mPropertyNodesVerifier = new PropertyNodesVerifier(mTestCase);
+        }
+        PropertyNodesVerifierElem elem =
+                mPropertyNodesVerifier.addPropertyNodesVerifierElem();
+        elem.addExpectedNodeWithOrder("VERSION", (mIsV30 ? "3.0" : "2.1"));
+
+        return elem;
+    }
+
+    public PropertyNodesVerifierElem addPropertyNodesVerifierElemWithEmptyName() {
+        if (!mInitialized) {
+            mTestCase.fail("Not initialized");
+        }
+        PropertyNodesVerifierElem elem = addPropertyNodesVerifierElem();
+        if (mIsV30) {
+            elem.addExpectedNodeWithOrder("N", "").addExpectedNodeWithOrder("FN", "");
+        } else if (mIsDoCoMo) {
+            elem.addExpectedNodeWithOrder("N", "");
+        }
+        return elem;
+    }
+
+    public LineVerifierElem addLineVerifierElem() {
+        if (!mInitialized) {
+            mTestCase.fail("Not initialized");
+        }
+        if (mLineVerifier == null) {
+            mLineVerifier = new LineVerifier(mTestCase, mVCardType);
+        }
+        return mLineVerifier.addLineVerifierElem();
+    }
+
+    public ContentValuesVerifierElem addContentValuesVerifierElem() {
+        if (!mInitialized) {
+            mTestCase.fail("Not initialized");
+        }
+        if (mContentValuesVerifier == null) {
+            mContentValuesVerifier = new ContentValuesVerifier();
+        }
+
+        return mContentValuesVerifier.addElem(mTestCase);
+    }
+
+    private void verifyOneVCard(final String vcard) {
+        // Log.d("@@@", vcard);
+        final VCardInterpreter builder;
+        if (mContentValuesVerifier != null) {
+            final VNodeBuilder vnodeBuilder = mPropertyNodesVerifier;
+            final VCardEntryConstructor vcardDataBuilder =
+                    new VCardEntryConstructor(mVCardType);
+            vcardDataBuilder.addEntryHandler(mContentValuesVerifier);
+            if (mPropertyNodesVerifier != null) {
+                builder = new VCardInterpreterCollection(Arrays.asList(
+                        mPropertyNodesVerifier, vcardDataBuilder));
+            } else {
+                builder = vnodeBuilder;
+            }
+        } else {
+            if (mPropertyNodesVerifier != null) {
+                builder = mPropertyNodesVerifier;
+            } else {
+                return;
+            }
+        }
+
+        final VCardParser parser =
+                (mIsV30 ? new VCardParser_V30(true) : new VCardParser_V21());
+        InputStream is = null;
+        try {
+            String charset =
+                (VCardConfig.usesShiftJis(mVCardType) ? "SHIFT_JIS" : "UTF-8");
+            is = new ByteArrayInputStream(vcard.getBytes(charset));
+            mTestCase.assertEquals(true, parser.parse(is, null, builder));
+        } catch (IOException e) {
+            mTestCase.fail("Unexpected IOException: " + e.getMessage());
+        } catch (VCardException e) {
+            mTestCase.fail("Unexpected VCardException: " + e.getMessage());
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    public void verify() {
+        if (!mInitialized) {
+            mTestCase.fail("Not initialized.");
+        }
+        if (mVerified) {
+            mTestCase.fail("verify() was called twice.");
+        }
+        if (mInputStream != null) {
+            try {
+                verifyForImportTest();
+            } catch (IOException e) {
+                mTestCase.fail("IOException was thrown: " + e.getMessage());
+            } catch (VCardException e) {
+                mTestCase.fail("VCardException was thrown: " + e.getMessage());
+            }
+        } else if (mExportTestResolver != null){
+            verifyForExportTest();
+        } else {
+            mTestCase.fail("No input is determined");
+        }
+        mVerified = true;
+    }
+
+    private void verifyForImportTest() throws IOException, VCardException {
+        if (mLineVerifier != null) {
+            mTestCase.fail("Not supported now.");
+        }
+        if (mContentValuesVerifier != null) {
+            mContentValuesVerifier.verify(mInputStream, mVCardType);
+        }
+    }
+
+    public static EntityIterator mockGetEntityIteratorMethod(
+            final ContentResolver resolver,
+            final Uri uri, final String selection,
+            final String[] selectionArgs, final String sortOrder) {
+        final ContentProvider provider =
+            resolver.acquireContentProviderClient(uri).getLocalContentProvider();
+        return ((ExportTestProvider)provider).queryEntities(
+                uri, selection, selectionArgs, sortOrder);
+    }
+
+    private Method getMockGetEntityIteratorMethod()
+            throws SecurityException, NoSuchMethodException {
+        return this.getClass().getMethod("mockGetEntityIteratorMethod",
+                ContentResolver.class, Uri.class, String.class, String[].class, String.class);
+    }
+
+    private void verifyForExportTest() {
+       final VCardComposer composer =
+            new VCardComposer(new CustomMockContext(mExportTestResolver), mVCardType);
+        composer.addHandler(mLineVerifier);
+        composer.addHandler(mVCardVerifierInternal);
+        if (!composer.init(VCardComposer.CONTACTS_TEST_CONTENT_URI, null, null, null)) {
+            mTestCase.fail("init() failed. Reason: " + composer.getErrorReason());
+        }
+        mTestCase.assertFalse(composer.isAfterLast());
+        try {
+            while (!composer.isAfterLast()) {
+                try {
+                    final Method mockGetEntityIteratorMethod = getMockGetEntityIteratorMethod();
+                    mTestCase.assertTrue(
+                            composer.createOneEntry(getMockGetEntityIteratorMethod()));
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    mTestCase.fail();
+                }
+            }
+        } finally {
+            composer.terminate();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VNode.java b/core/tests/coretests/src/android/pim/vcard/VNode.java
new file mode 100644
index 0000000..79f10dc
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VNode.java
@@ -0,0 +1,30 @@
+/*
+ * 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.pim.vcard;
+
+import java.util.ArrayList;
+
+/**
+ * Previously used in main vCard handling code but now exists only for testing.
+ */
+public class VNode {
+    public String VName;
+
+    public ArrayList<PropertyNode> propList = new ArrayList<PropertyNode>();
+
+    /** 0:parse over. 1:parsing. */
+    public int parseStatus = 1;
+}
diff --git a/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java b/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java
new file mode 100644
index 0000000..0e6c325
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/vcard/VNodeBuilder.java
@@ -0,0 +1,314 @@
+/*
+ * 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.pim.vcard;
+
+import android.content.ContentValues;
+import android.pim.vcard.VCardInterpreter;
+import android.pim.vcard.VCardConfig;
+import android.util.CharsetUtils;
+import android.util.Log;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.net.QuotedPrintableCodec;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Store the parse result to custom datastruct: VNode, PropertyNode
+ * Maybe several vcard instance, so use vNodeList to store.
+ * VNode: standy by a vcard instance.
+ * PropertyNode: standy by a property line of a card.
+ *
+ * Previously used in main vCard handling code but now exists only for testing.
+ */
+public class VNodeBuilder implements VCardInterpreter {
+    static private String LOG_TAG = "VNodeBuilder"; 
+    
+    /**
+     * If there's no other information available, this class uses this charset for encoding
+     * byte arrays.
+     */
+    static public String TARGET_CHARSET = "UTF-8"; 
+    
+    /** type=VNode */
+    public List<VNode> vNodeList = new ArrayList<VNode>();
+    private int mNodeListPos = 0;
+    private VNode mCurrentVNode;
+    private PropertyNode mCurrentPropNode;
+    private String mCurrentParamType;
+    
+    /**
+     * The charset using which VParser parses the text.
+     */
+    private String mSourceCharset;
+    
+    /**
+     * The charset with which byte array is encoded to String.
+     */
+    private String mTargetCharset;
+    
+    private boolean mStrictLineBreakParsing;
+    
+    public VNodeBuilder() {
+        this(VCardConfig.DEFAULT_CHARSET, TARGET_CHARSET, false);
+    }
+
+    public VNodeBuilder(String charset, boolean strictLineBreakParsing) {
+        this(null, charset, strictLineBreakParsing);
+    }
+    
+    /**
+     * @hide sourceCharset is temporal. 
+     */
+    public VNodeBuilder(String sourceCharset, String targetCharset,
+            boolean strictLineBreakParsing) {
+        if (sourceCharset != null) {
+            mSourceCharset = sourceCharset;
+        } else {
+            mSourceCharset = VCardConfig.DEFAULT_CHARSET;
+        }
+        if (targetCharset != null) {
+            mTargetCharset = targetCharset;
+        } else {
+            mTargetCharset = TARGET_CHARSET;
+        }
+        mStrictLineBreakParsing = strictLineBreakParsing;
+    }
+
+    public void start() {
+    }
+
+    public void end() {
+    }
+
+    // Note: I guess that this code assumes the Record may nest like this:
+    // START:VPOS
+    // ...
+    // START:VPOS2
+    // ...
+    // END:VPOS2
+    // ...
+    // END:VPOS
+    //
+    // However the following code has a bug.
+    // When error occurs after calling startRecord(), the entry which is probably
+    // the cause of the error remains to be in vNodeList, while endRecord() is not called.
+    //
+    // I leave this code as is since I'm not familiar with vcalendar specification.
+    // But I believe we should refactor this code in the future.
+    // Until this, the last entry has to be removed when some error occurs.
+    public void startEntry() {
+        VNode vnode = new VNode();
+        vnode.parseStatus = 1;
+        vnode.VName = "VCARD";
+        // I feel this should be done in endRecord(), but it cannot be done because of
+        // the reason above.
+        vNodeList.add(vnode);
+        mNodeListPos = vNodeList.size() - 1;
+        mCurrentVNode = vNodeList.get(mNodeListPos);
+    }
+
+    public void endEntry() {
+        VNode endNode = vNodeList.get(mNodeListPos);
+        endNode.parseStatus = 0;
+        while(mNodeListPos > 0){
+            mNodeListPos--;
+            if((vNodeList.get(mNodeListPos)).parseStatus == 1)
+                break;
+        }
+        mCurrentVNode = vNodeList.get(mNodeListPos);
+    }
+
+    public void startProperty() {
+        mCurrentPropNode = new PropertyNode();
+    }
+
+    public void endProperty() {
+        mCurrentVNode.propList.add(mCurrentPropNode);
+    }
+    
+    public void propertyName(String name) {
+        mCurrentPropNode.propName = name;
+    }
+
+    // Used only in VCard.
+    public void propertyGroup(String group) {
+        mCurrentPropNode.propGroupSet.add(group);
+    }
+    
+    public void propertyParamType(String type) {
+        mCurrentParamType = type;
+    }
+
+    public void propertyParamValue(String value) {
+        if (mCurrentParamType == null ||
+                mCurrentParamType.equalsIgnoreCase("TYPE")) {
+            mCurrentPropNode.paramMap_TYPE.add(value);
+        } else {
+            mCurrentPropNode.paramMap.put(mCurrentParamType, value);
+        }
+
+        mCurrentParamType = null;
+    }
+
+    private String encodeString(String originalString, String targetCharset) {
+        if (mSourceCharset.equalsIgnoreCase(targetCharset)) {
+            return originalString;
+        }
+        Charset charset = Charset.forName(mSourceCharset);
+        ByteBuffer byteBuffer = charset.encode(originalString);
+        // byteBuffer.array() "may" return byte array which is larger than
+        // byteBuffer.remaining(). Here, we keep on the safe side.
+        byte[] bytes = new byte[byteBuffer.remaining()];
+        byteBuffer.get(bytes);
+        try {
+            return new String(bytes, targetCharset);
+        } catch (UnsupportedEncodingException e) {
+            Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+            return null;
+        }
+    }
+    
+    private String handleOneValue(String value, String targetCharset, String encoding) {
+        if (encoding != null) {
+            encoding = encoding.toUpperCase();
+            if (encoding.equals("BASE64") || encoding.equals("B")) {
+                // Assume BASE64 is used only when the number of values is 1.
+                mCurrentPropNode.propValue_bytes =
+                    Base64.decodeBase64(value.getBytes());
+                return value;
+            } else if (encoding.equals("QUOTED-PRINTABLE")) {
+                String quotedPrintable = value
+                .replaceAll("= ", " ").replaceAll("=\t", "\t");
+                String[] lines;
+                if (mStrictLineBreakParsing) {
+                    lines = quotedPrintable.split("\r\n");
+                } else {
+                    StringBuilder builder = new StringBuilder();
+                    int length = quotedPrintable.length();
+                    ArrayList<String> list = new ArrayList<String>();
+                    for (int i = 0; i < length; i++) {
+                        char ch = quotedPrintable.charAt(i);
+                        if (ch == '\n') {
+                            list.add(builder.toString());
+                            builder = new StringBuilder();
+                        } else if (ch == '\r') {
+                            list.add(builder.toString());
+                            builder = new StringBuilder();
+                            if (i < length - 1) {
+                                char nextCh = quotedPrintable.charAt(i + 1);
+                                if (nextCh == '\n') {
+                                    i++;
+                                }
+                            }
+                        } else {
+                            builder.append(ch);
+                        }
+                    }
+                    String finalLine = builder.toString();
+                    if (finalLine.length() > 0) {
+                        list.add(finalLine);
+                    }
+                    lines = list.toArray(new String[0]);
+                }
+                StringBuilder builder = new StringBuilder();
+                for (String line : lines) {
+                    if (line.endsWith("=")) {
+                        line = line.substring(0, line.length() - 1);
+                    }
+                    builder.append(line);
+                }
+                byte[] bytes;
+                try {
+                    bytes = builder.toString().getBytes(mSourceCharset);
+                } catch (UnsupportedEncodingException e1) {
+                    Log.e(LOG_TAG, "Failed to encode: charset=" + mSourceCharset);
+                    bytes = builder.toString().getBytes();
+                }
+                
+                try {
+                    bytes = QuotedPrintableCodec.decodeQuotedPrintable(bytes);
+                } catch (DecoderException e) {
+                    Log.e(LOG_TAG, "Failed to decode quoted-printable: " + e);
+                    return "";
+                }
+
+                try {
+                    return new String(bytes, targetCharset);
+                } catch (UnsupportedEncodingException e) {
+                    Log.e(LOG_TAG, "Failed to encode: charset=" + targetCharset);
+                    return new String(bytes);
+                }
+            }
+            // Unknown encoding. Fall back to default.
+        }
+        return encodeString(value, targetCharset);
+    }
+    
+    public void propertyValues(List<String> values) {
+        if (values == null || values.size() == 0) {
+            mCurrentPropNode.propValue_bytes = null;
+            mCurrentPropNode.propValue_vector.clear();
+            mCurrentPropNode.propValue_vector.add("");
+            mCurrentPropNode.propValue = "";
+            return;
+        }
+        
+        ContentValues paramMap = mCurrentPropNode.paramMap;
+        
+        String targetCharset = CharsetUtils.nameForDefaultVendor(paramMap.getAsString("CHARSET")); 
+        String encoding = paramMap.getAsString("ENCODING"); 
+        
+        if (targetCharset == null || targetCharset.length() == 0) {
+            targetCharset = mTargetCharset;
+        }
+        
+        for (String value : values) {
+            mCurrentPropNode.propValue_vector.add(
+                    handleOneValue(value, targetCharset, encoding));
+        }
+
+        mCurrentPropNode.propValue = listToString(mCurrentPropNode.propValue_vector);
+    }
+
+    private String listToString(List<String> list){
+        int size = list.size();
+        if (size > 1) {
+            StringBuilder typeListB = new StringBuilder();
+            for (String type : list) {
+                typeListB.append(type).append(";");
+            }
+            int len = typeListB.length();
+            if (len > 0 && typeListB.charAt(len - 1) == ';') {
+                return typeListB.substring(0, len - 1);
+            }
+            return typeListB.toString();
+        } else if (size == 1) {
+            return list.get(0);
+        } else {
+            return "";
+        }
+    }
+    
+    public String getResult(){
+        return null;
+    }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
new file mode 100644
index 0000000..f82d79a
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -0,0 +1,147 @@
+/*
+ * 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.provider;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+/** Unit test for SettingsProvider. */
+public class SettingsProviderTest extends AndroidTestCase {
+    @MediumTest
+    public void testNameValueCache() {
+        ContentResolver r = getContext().getContentResolver();
+        Settings.Secure.putString(r, "test_service", "Value");
+        assertEquals("Value", Settings.Secure.getString(r, "test_service"));
+
+        // Make sure the value can be overwritten.
+        Settings.Secure.putString(r, "test_service", "New");
+        assertEquals("New", Settings.Secure.getString(r, "test_service"));
+
+        // Also that delete works.
+        assertEquals(1, r.delete(Settings.Secure.getUriFor("test_service"), null, null));
+        assertEquals(null, Settings.Secure.getString(r, "test_service"));
+
+        // Try all the same things in the System table
+        Settings.System.putString(r, "test_setting", "Value");
+        assertEquals("Value", Settings.System.getString(r, "test_setting"));
+
+        Settings.System.putString(r, "test_setting", "New");
+        assertEquals("New", Settings.System.getString(r, "test_setting"));
+
+        assertEquals(1, r.delete(Settings.System.getUriFor("test_setting"), null, null));
+        assertEquals(null, Settings.System.getString(r, "test_setting"));
+    }
+
+    @MediumTest
+    public void testRowNameContentUri() {
+        ContentResolver r = getContext().getContentResolver();
+
+        assertEquals("content://settings/system/test_setting",
+                Settings.System.getUriFor("test_setting").toString());
+        assertEquals("content://settings/gservices/test_service",
+                Settings.Secure.getUriFor("test_service").toString());
+
+        // These tables use the row name (not ID) as their content URI.
+        Uri tables[] = { Settings.System.CONTENT_URI, Settings.Secure.CONTENT_URI };
+        for (Uri table : tables) {
+            ContentValues v = new ContentValues();
+            v.put(Settings.System.NAME, "test_key");
+            v.put(Settings.System.VALUE, "Test");
+            Uri uri = r.insert(table, v);
+            assertEquals(table.toString() + "/test_key", uri.toString());
+
+            // Query with a specific URI and no WHERE clause succeeds.
+            Cursor c = r.query(uri, null, null, null, null);
+            try {
+                assertTrue(c.moveToNext());
+                assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
+                assertEquals("Test", c.getString(c.getColumnIndex(Settings.System.VALUE)));
+                assertFalse(c.moveToNext());
+            } finally {
+                c.close();
+            }
+
+            // Query with a specific URI and a WHERE clause fails.
+            try {
+                r.query(uri, null, "1", null, null);
+                fail("UnsupportedOperationException expected");
+            } catch (UnsupportedOperationException e) {
+                if (!e.toString().contains("WHERE clause")) throw e;
+            }
+
+            // Query with a tablewide URI and a WHERE clause succeeds.
+            c = r.query(table, null, "name='test_key'", null, null);
+            try {
+                assertTrue(c.moveToNext());
+                assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
+                assertEquals("Test", c.getString(c.getColumnIndex(Settings.System.VALUE)));
+                assertFalse(c.moveToNext());
+            } finally {
+                c.close();
+            }
+
+            v = new ContentValues();
+            v.put(Settings.System.VALUE, "Toast");
+            assertEquals(1, r.update(uri, v, null, null));
+
+            c = r.query(uri, null, null, null, null);
+            try {
+                assertTrue(c.moveToNext());
+                assertEquals("test_key", c.getString(c.getColumnIndex(Settings.System.NAME)));
+                assertEquals("Toast", c.getString(c.getColumnIndex(Settings.System.VALUE)));
+                assertFalse(c.moveToNext());
+            } finally {
+                c.close();
+            }
+
+            assertEquals(1, r.delete(uri, null, null));
+        }
+
+        assertEquals(null, Settings.System.getString(r, "test_key"));
+        assertEquals(null, Settings.Secure.getString(r, "test_key"));
+    }
+
+    @MediumTest
+    public void testRowNumberContentUri() {
+        ContentResolver r = getContext().getContentResolver();
+
+        // The bookmarks table (and everything else) uses standard row number content URIs.
+        Uri uri = Settings.Bookmarks.add(r, new Intent("TEST"),
+                "Test Title", "Test Folder", '*', 123);
+
+        assertTrue(ContentUris.parseId(uri) > 0);
+
+        assertEquals("TEST", Settings.Bookmarks.getIntentForShortcut(r, '*').getAction());
+
+        ContentValues v = new ContentValues();
+        v.put(Settings.Bookmarks.INTENT, "#Intent;action=TOAST;end");
+        assertEquals(1, r.update(uri, v, null, null));
+
+        assertEquals("TOAST", Settings.Bookmarks.getIntentForShortcut(r, '*').getAction());
+
+        assertEquals(1, r.delete(uri, null, null));
+
+        assertEquals(null, Settings.Bookmarks.getIntentForShortcut(r, '*'));
+    }
+}
diff --git a/core/tests/coretests/src/android/provider/SmsProviderTest.java b/core/tests/coretests/src/android/provider/SmsProviderTest.java
new file mode 100644
index 0000000..c8ed728
--- /dev/null
+++ b/core/tests/coretests/src/android/provider/SmsProviderTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.provider;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Telephony.Sms;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.util.GregorianCalendar;
+
+public class SmsProviderTest extends AndroidTestCase {
+
+    @LargeTest
+    public void testProvider() throws Exception {
+        // This test does the following
+        //  1. Insert 10 messages from the same number at different times.
+        //
+        //  . Delete the messages and make sure that they were deleted.
+
+        long now = System.currentTimeMillis();
+
+        Uri[] urls = new Uri[10];
+        String[] dates = new String[]{
+                Long.toString(new GregorianCalendar(1970, 1, 1, 0, 0, 0).getTimeInMillis()),
+                Long.toString(new GregorianCalendar(1971, 2, 13, 16, 35, 3).getTimeInMillis()),
+                Long.toString(new GregorianCalendar(1978, 10, 22, 0, 1, 0).getTimeInMillis()),
+                Long.toString(new GregorianCalendar(1980, 1, 11, 10, 22, 30).getTimeInMillis()),
+                Long.toString(now - (5 * 24 * 60 * 60 * 1000)),
+                Long.toString(now - (2 * 24 * 60 * 60 * 1000)),
+                Long.toString(now - (5 * 60 * 60 * 1000)),
+                Long.toString(now - (30 * 60 * 1000)),
+                Long.toString(now - (5 * 60 * 1000)),
+                Long.toString(now)
+        };
+
+        ContentValues map = new ContentValues();
+        map.put("address", "+15045551337");
+        map.put("read", 0);
+
+        ContentResolver contentResolver = mContext.getContentResolver();
+
+        for (int i = 0; i < urls.length; i++) {
+            map.put("body", "Test " + i + " !");
+            map.put("date", dates[i]);
+            urls[i] = contentResolver.insert(Sms.Inbox.CONTENT_URI, map);
+            assertNotNull(urls[i]);
+        }
+
+        Cursor c = contentResolver.query(Sms.Inbox.CONTENT_URI, null, null, null, "date");
+
+        //DatabaseUtils.dumpCursor(c);
+
+        for (Uri url : urls) {
+            int count = contentResolver.delete(url, null, null);
+            assertEquals(1, count);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/text/SpannedTest.java b/core/tests/coretests/src/android/text/SpannedTest.java
new file mode 100644
index 0000000..1c22cf9
--- /dev/null
+++ b/core/tests/coretests/src/android/text/SpannedTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.text;
+
+import android.graphics.Typeface;
+import android.os.Parcel;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.*;
+import android.text.style.*;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+/**
+ * SpannedTest tests some features of Spanned
+ */
+public class SpannedTest extends TestCase {
+    private int mExpect;
+
+    @SmallTest
+    public void testSpannableString() throws Exception {
+        checkPriority(new SpannableString("the quick brown fox"));
+    }
+
+    @SmallTest
+    public void testSpannableStringBuilder() throws Exception {
+        checkPriority2(new SpannableStringBuilder("the quick brown fox"));
+    }
+
+    @SmallTest
+    public void testAppend() throws Exception {
+        Object o = new Object();
+        SpannableString ss = new SpannableString("Test");
+        ss.setSpan(o, 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        SpannableStringBuilder ssb = new SpannableStringBuilder();
+        ssb.append(ss);
+        assertEquals(0, ssb.getSpanStart(o));
+        assertEquals(4, ssb.getSpanEnd(o));
+        assertEquals(1, ssb.getSpans(0, 4, Object.class).length);
+
+        ssb.insert(0, ss);
+        assertEquals(4, ssb.getSpanStart(o));
+        assertEquals(8, ssb.getSpanEnd(o));
+        assertEquals(0, ssb.getSpans(0, 4, Object.class).length);
+        assertEquals(1, ssb.getSpans(4, 8, Object.class).length);
+    }
+
+    @SmallTest
+    public void testWrapParcel() {
+        SpannableString s = new SpannableString("Hello there world");
+        CharacterStyle mark = new StyleSpan(Typeface.BOLD);
+        s.setSpan(mark, 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        s.setSpan(CharacterStyle.wrap(mark), 3, 7,
+                  Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        s.setSpan(new TextAppearanceSpan("mono", 0, -1, null, null), 7, 8,
+                  Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        s.setSpan(CharacterStyle.wrap(new TypefaceSpan("mono")), 8, 9,
+                  Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        Parcel p = Parcel.obtain();
+        TextUtils.writeToParcel(s, p, 0);
+        p.setDataPosition(0);
+
+        Spanned s2 = (Spanned) TextUtils.CHAR_SEQUENCE_CREATOR.
+                        createFromParcel(p);
+        StyleSpan[] style;
+
+        style = s2.getSpans(1, 2, StyleSpan.class);
+        assertEquals(1, style.length);
+        assertEquals(1, s2.getSpanStart(style[0]));
+        assertEquals(2, s2.getSpanEnd(style[0]));
+
+        style = s2.getSpans(3, 7, StyleSpan.class);
+        assertEquals(1, style.length);
+        assertEquals(3, s2.getSpanStart(style[0]));
+        assertEquals(7, s2.getSpanEnd(style[0]));
+
+        TextAppearanceSpan[] appearance = s2.getSpans(7, 8,
+                                                TextAppearanceSpan.class);
+        assertEquals(1, appearance.length);
+        assertEquals(7, s2.getSpanStart(appearance[0]));
+        assertEquals(8, s2.getSpanEnd(appearance[0]));
+
+        TypefaceSpan[] tf = s2.getSpans(8, 9, TypefaceSpan.class);
+        assertEquals(1, tf.length);
+        assertEquals(8, s2.getSpanStart(tf[0]));
+        assertEquals(9, s2.getSpanEnd(tf[0]));
+    }
+
+    private void checkPriority(Spannable s) {
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+                                      (5 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+                                      (10 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+                                      (0 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+                                      (15 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+                                      (3 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+                                      (6 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE |
+                                      (0 << Spannable.SPAN_PRIORITY_SHIFT));
+
+        Object[] spans = s.getSpans(0, s.length(), Object.class);
+
+        for (int i = 0; i < spans.length - 1; i++) {
+            assertEquals((s.getSpanFlags(spans[i]) & Spanned.SPAN_PRIORITY) >=
+                         (s.getSpanFlags(spans[i + 1]) & Spanned.SPAN_PRIORITY),
+                         true);
+        }
+
+        mExpect = 0;
+
+        s.setSpan(new Watcher(2), 0, s.length(), 
+                  Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+                  (2 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Watcher(4), 0, s.length(), 
+                  Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+                  (4 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Watcher(1), 0, s.length(), 
+                  Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+                  (1 << Spannable.SPAN_PRIORITY_SHIFT));
+        s.setSpan(new Watcher(3), 0, s.length(), 
+                  Spannable.SPAN_INCLUSIVE_INCLUSIVE |
+                  (3 << Spannable.SPAN_PRIORITY_SHIFT));
+
+        mExpect = 4;
+        s.setSpan(new Object(), 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        assertEquals(mExpect, 0);
+    }
+
+    private void checkPriority2(SpannableStringBuilder ssb) {
+        checkPriority(ssb);
+
+        mExpect = 4;
+        ssb.insert(3, "something");
+        assertEquals(mExpect, 0);
+    }
+
+    private class Watcher implements SpanWatcher, TextWatcher {
+        private int mSequence;
+
+        public Watcher(int sequence) {
+            mSequence = sequence;
+        }
+
+        public void onSpanChanged(Spannable b, Object o, int s, int e,
+                                  int st, int en) { }
+        public void onSpanRemoved(Spannable b, Object o, int s, int e) { }
+
+        public void onSpanAdded(Spannable b, Object o, int s, int e) {
+            if (mExpect != 0) {
+                assertEquals(mSequence, mExpect);
+                mExpect = mSequence - 1;
+            }
+        }
+
+        public void beforeTextChanged(CharSequence s, int start, int count,
+                                      int after) { }
+        public void onTextChanged(CharSequence s, int start, int before,
+                                      int count) {
+            if (mExpect != 0) {
+                assertEquals(mSequence, mExpect);
+                mExpect = mSequence - 1;
+            }
+        }
+
+        public void afterTextChanged(Editable s) { }
+    }
+}
diff --git a/core/tests/coretests/src/android/text/TextLayoutTest.java b/core/tests/coretests/src/android/text/TextLayoutTest.java
new file mode 100644
index 0000000..6cf3000
--- /dev/null
+++ b/core/tests/coretests/src/android/text/TextLayoutTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.text;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.DynamicLayout;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import junit.framework.TestCase;
+
+
+public class TextLayoutTest extends TestCase {
+
+    protected String mString;
+    protected TextPaint mPaint;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        mString = "The quick brown fox";
+        mPaint = new TextPaint();
+    }
+
+    @SmallTest
+    public void testStaticLayout() throws Exception {
+        Layout l = new StaticLayout(mString, mPaint, 200,
+                Layout.Alignment.ALIGN_NORMAL, 1, 0,
+                true);
+    }
+
+    @SmallTest
+    public void testDynamicLayoutTest() throws Exception {
+        Layout l = new DynamicLayout(mString, mPaint, 200,
+                Layout.Alignment.ALIGN_NORMAL, 1, 0,
+                true);
+    }
+}
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
new file mode 100644
index 0000000..5b427be
--- /dev/null
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.text;
+
+import android.graphics.Paint;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
+import android.test.MoreAsserts;
+
+import com.android.common.Rfc822Validator;
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * TextUtilsTest tests {@link TextUtils}.
+ */
+public class TextUtilsTest extends TestCase {
+
+    @SmallTest
+    public void testBasic() throws Exception {
+        assertEquals("", TextUtils.concat());
+        assertEquals("foo", TextUtils.concat("foo"));
+        assertEquals("foobar", TextUtils.concat("foo", "bar"));
+        assertEquals("foobarbaz", TextUtils.concat("foo", "bar", "baz"));
+
+        SpannableString foo = new SpannableString("foo");
+        foo.setSpan("foo", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+        SpannableString bar = new SpannableString("bar");
+        bar.setSpan("bar", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+        SpannableString baz = new SpannableString("baz");
+        baz.setSpan("baz", 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+        assertEquals("foo", TextUtils.concat(foo).toString());
+        assertEquals("foobar", TextUtils.concat(foo, bar).toString());
+        assertEquals("foobarbaz", TextUtils.concat(foo, bar, baz).toString());
+
+        assertEquals(1, ((Spanned) TextUtils.concat(foo)).getSpanStart("foo"));
+
+        assertEquals(1, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("foo"));
+        assertEquals(4, ((Spanned) TextUtils.concat(foo, bar)).getSpanStart("bar"));
+
+        assertEquals(1, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("foo"));
+        assertEquals(4, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("bar"));
+        assertEquals(7, ((Spanned) TextUtils.concat(foo, bar, baz)).getSpanStart("baz"));
+
+        assertTrue(TextUtils.concat("foo", "bar") instanceof String);
+        assertTrue(TextUtils.concat(foo, bar) instanceof SpannedString);
+    }
+
+    @SmallTest
+    public void testTemplateString() throws Exception {
+        CharSequence result;
+
+        result = TextUtils.expandTemplate("This is a ^1 of the ^2 broadcast ^3.",
+                                          "test", "emergency", "system");
+        assertEquals("This is a test of the emergency broadcast system.",
+                     result.toString());
+
+        result = TextUtils.expandTemplate("^^^1^^^2^3^a^1^^b^^^c",
+                                          "one", "two", "three");
+        assertEquals("^one^twothree^aone^b^^c",
+                     result.toString());
+
+        result = TextUtils.expandTemplate("^");
+        assertEquals("^", result.toString());
+
+        result = TextUtils.expandTemplate("^^");
+        assertEquals("^", result.toString());
+
+        result = TextUtils.expandTemplate("^^^");
+        assertEquals("^^", result.toString());
+
+        result = TextUtils.expandTemplate("shorter ^1 values ^2.", "a", "");
+        assertEquals("shorter a values .", result.toString());
+
+        try {
+            TextUtils.expandTemplate("Only ^1 value given, but ^2 used.", "foo");
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+
+        try {
+            TextUtils.expandTemplate("^1 value given, and ^0 used.", "foo");
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+
+        result = TextUtils.expandTemplate("^1 value given, and ^9 used.",
+                                          "one", "two", "three", "four", "five",
+                                          "six", "seven", "eight", "nine");
+        assertEquals("one value given, and nine used.", result.toString());
+
+        try {
+            TextUtils.expandTemplate("^1 value given, and ^10 used.",
+                                     "one", "two", "three", "four", "five",
+                                     "six", "seven", "eight", "nine", "ten");
+            fail();
+        } catch (IllegalArgumentException e) {
+        }
+
+        // putting carets in the values: expansion is not recursive.
+
+        result = TextUtils.expandTemplate("^2", "foo", "^^");
+        assertEquals("^^", result.toString());
+
+        result = TextUtils.expandTemplate("^^2", "foo", "1");
+        assertEquals("^2", result.toString());
+
+        result = TextUtils.expandTemplate("^1", "value with ^2 in it", "foo");
+        assertEquals("value with ^2 in it", result.toString());
+    }
+
+    /** Fail unless text+spans contains a span 'spanName' with the given start and end. */
+    private void checkContains(Spanned text, String[] spans, String spanName,
+                               int start, int end) throws Exception {
+        for (String i: spans) {
+            if (i.equals(spanName)) {
+                assertEquals(start, text.getSpanStart(i));
+                assertEquals(end, text.getSpanEnd(i));
+                return;
+            }
+        }
+        fail();
+    }
+
+    @SmallTest
+    public void testTemplateSpan() throws Exception {
+        SpannableString template;
+        Spanned result;
+        String[] spans;
+
+        // ordinary replacement
+
+        template = new SpannableString("a^1b");
+        template.setSpan("before", 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        template.setSpan("during", 1, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        template.setSpan("after", 3, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        template.setSpan("during+after", 1, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        result = (Spanned) TextUtils.expandTemplate(template, "foo");
+        assertEquals(5, result.length());
+        spans = result.getSpans(0, result.length(), String.class);
+
+        // value is one character longer, so span endpoints should change.
+        assertEquals(4, spans.length);
+        checkContains(result, spans, "before", 0, 1);
+        checkContains(result, spans, "during", 1, 4);
+        checkContains(result, spans, "after", 4, 5);
+        checkContains(result, spans, "during+after", 1, 5);
+
+
+        // replacement with empty string
+
+        result = (Spanned) TextUtils.expandTemplate(template, "");
+        assertEquals(2, result.length());
+        spans = result.getSpans(0, result.length(), String.class);
+
+        // the "during" span should disappear.
+        assertEquals(3, spans.length);
+        checkContains(result, spans, "before", 0, 1);
+        checkContains(result, spans, "after", 1, 2);
+        checkContains(result, spans, "during+after", 1, 2);
+    }
+
+    @SmallTest
+    public void testStringSplitterSimple() {
+        stringSplitterTestHelper("a,b,cde", new String[] {"a", "b", "cde"});
+    }
+
+    @SmallTest
+    public void testStringSplitterEmpty() {
+        stringSplitterTestHelper("", new String[] {});
+    }
+
+    @SmallTest
+    public void testStringSplitterWithLeadingEmptyString() {
+        stringSplitterTestHelper(",a,b,cde", new String[] {"", "a", "b", "cde"});
+    }
+
+    @SmallTest
+    public void testStringSplitterWithInternalEmptyString() {
+        stringSplitterTestHelper("a,b,,cde", new String[] {"a", "b", "", "cde"});
+    }
+
+    @SmallTest
+    public void testStringSplitterWithTrailingEmptyString() {
+        // A single trailing emtpy string should be ignored.
+        stringSplitterTestHelper("a,b,cde,", new String[] {"a", "b", "cde"});
+    }
+
+    private void stringSplitterTestHelper(String string, String[] expectedStrings) {
+        TextUtils.StringSplitter splitter = new TextUtils.SimpleStringSplitter(',');
+        splitter.setString(string);
+        List<String> strings = Lists.newArrayList();
+        for (String s : splitter) {
+            strings.add(s);
+        }
+        MoreAsserts.assertEquals(expectedStrings, strings.toArray(new String[]{}));
+    }
+
+    @SmallTest
+    public void testTrim() {
+        String[] strings = { "abc", " abc", "  abc", "abc ", "abc  ",
+                             " abc ", "  abc  ", "\nabc\n", "\nabc", "abc\n" };
+
+        for (String s : strings) {
+            assertEquals(s.trim().length(), TextUtils.getTrimmedLength(s));
+        }
+    }
+
+    //==============================================================================================
+    // Email validator
+    //==============================================================================================
+    
+    @SmallTest
+    public void testEmailValidator() {
+        Rfc822Validator validator = new Rfc822Validator("gmail.com");
+        String[] validEmails = new String[] {
+            "a@b.com", "a@b.fr", "a+b@c.com", "a@b.info",
+        };
+        
+        for (String email : validEmails) {
+            assertTrue(email + " should be a valid email address", validator.isValid(email));
+        }
+        
+        String[] invalidEmails = new String[] {
+            "a", "a@b", "a b", "a@b.12"
+        };
+
+        for (String email : invalidEmails) {
+            assertFalse(email + " should not be a valid email address", validator.isValid(email));
+        }
+        
+        Map<String, String> fixes = Maps.newHashMap();
+        fixes.put("a", "<a@gmail.com>");
+        fixes.put("a b", "<ab@gmail.com>");
+        fixes.put("a@b", "<a@b>");
+        
+        for (Map.Entry<String, String> e : fixes.entrySet()) {
+            assertEquals(e.getValue(), validator.fixText(e.getKey()).toString());
+        }
+    }
+
+    @LargeTest
+    public void testEllipsize() {
+        CharSequence s1 = "The quick brown fox jumps over \u00FEhe lazy dog.";
+        CharSequence s2 = new Wrapper(s1);
+        Spannable s3 = new SpannableString(s1);
+        s3.setSpan(new StyleSpan(0), 5, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        TextPaint p = new TextPaint();
+        p.setFlags(p.getFlags() & ~p.DEV_KERN_TEXT_FLAG);
+
+        for (int i = 0; i < 100; i++) {
+            for (int j = 0; j < 3; j++) {
+                TextUtils.TruncateAt kind = null;
+
+                switch (j) {
+                case 0:
+                    kind = TextUtils.TruncateAt.START;
+                    break;
+
+                case 1:
+                    kind = TextUtils.TruncateAt.END;
+                    break;
+
+                case 2:
+                    kind = TextUtils.TruncateAt.MIDDLE;
+                    break;
+                }
+
+                String out1 = TextUtils.ellipsize(s1, p, i, kind).toString();
+                String out2 = TextUtils.ellipsize(s2, p, i, kind).toString();
+                String out3 = TextUtils.ellipsize(s3, p, i, kind).toString();
+
+                String keep1 = TextUtils.ellipsize(s1, p, i, kind, true, null).toString();
+                String keep2 = TextUtils.ellipsize(s2, p, i, kind, true, null).toString();
+                String keep3 = TextUtils.ellipsize(s3, p, i, kind, true, null).toString();
+
+                String trim1 = keep1.replace("\uFEFF", "");
+
+                // Are all normal output strings identical?
+                assertEquals("wid " + i + " pass " + j, out1, out2);
+                assertEquals("wid " + i + " pass " + j, out2, out3);
+
+                // Are preserved output strings identical?
+                assertEquals("wid " + i + " pass " + j, keep1, keep2);
+                assertEquals("wid " + i + " pass " + j, keep2, keep3);
+
+                // Does trimming padding from preserved yield normal?
+                assertEquals("wid " + i + " pass " + j, out1, trim1);
+
+                // Did preserved output strings preserve length?
+                assertEquals("wid " + i + " pass " + j, keep1.length(), s1.length());
+
+                // Does the output string actually fit in the space?
+                assertTrue("wid " + i + " pass " + j, p.measureText(out1) <= i);
+
+                // Is the padded output the same width as trimmed output?
+                assertTrue("wid " + i + " pass " + j, p.measureText(keep1) == p.measureText(out1));
+            }
+        }
+    }
+
+    /**
+     * CharSequence wrapper for testing the cases where text is copied into
+     * a char array instead of working from a String or a Spanned.
+     */
+    private static class Wrapper implements CharSequence {
+        private CharSequence mString;
+
+        public Wrapper(CharSequence s) {
+            mString = s;
+        }
+
+        public int length() {
+            return mString.length();
+        }
+
+        public char charAt(int off) {
+            return mString.charAt(off);
+        }
+
+        public String toString() {
+            return mString.toString();
+        }
+
+        public CharSequence subSequence(int start, int end) {
+            return new Wrapper(mString.subSequence(start, end));
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/text/format/TimeTest.java b/core/tests/coretests/src/android/text/format/TimeTest.java
new file mode 100644
index 0000000..489f58b
--- /dev/null
+++ b/core/tests/coretests/src/android/text/format/TimeTest.java
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.text.format;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import junit.framework.TestCase;
+
+public class TimeTest extends TestCase {
+
+    @SmallTest
+    public void testNormalize0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.parse("20060432T010203");
+        t.normalize(false /* use isDst */);
+//        System.out.println("got: " + t.year + '-'
+//                + t.month + '-' + t.monthDay
+//                + ' ' + t.hour + ':' + t.minute
+//                + ':' + t.second
+//                + "( " + t.isDst + ',' + t.gmtoff
+//                + ',' + t.weekDay
+//                + ',' + t.yearDay + ')');
+    }
+
+    private static class DateTest {
+        public int year1;
+        public int month1;
+        public int day1;
+        public int hour1;
+        public int minute1;
+        public int dst1;
+
+        public int offset;
+
+        public int year2;
+        public int month2;
+        public int day2;
+        public int hour2;
+        public int minute2;
+        public int dst2;
+
+        public DateTest(int year1, int month1, int day1, int hour1, int minute1, int dst1,
+                int offset, int year2, int month2, int day2, int hour2, int minute2,
+                int dst2) {
+            this.year1 = year1;
+            this.month1 = month1;
+            this.day1 = day1;
+            this.hour1 = hour1;
+            this.minute1 = minute1;
+            this.dst1 = dst1;
+            this.offset = offset;
+            this.year2 = year2;
+            this.month2 = month2;
+            this.day2 = day2;
+            this.hour2 = hour2;
+            this.minute2 = minute2;
+            this.dst2 = dst2;
+        }
+
+        public DateTest(int year1, int month1, int day1, int hour1, int minute1,
+                int offset, int year2, int month2, int day2, int hour2, int minute2) {
+            this.year1 = year1;
+            this.month1 = month1;
+            this.day1 = day1;
+            this.hour1 = hour1;
+            this.minute1 = minute1;
+            this.dst1 = -1;
+            this.offset = offset;
+            this.year2 = year2;
+            this.month2 = month2;
+            this.day2 = day2;
+            this.hour2 = hour2;
+            this.minute2 = minute2;
+            this.dst2 = -1;
+        }
+    }
+
+    // These tests assume that DST changes on Nov 4, 2007 at 2am (to 1am).
+
+    // The "offset" field in "dayTests" represents days.
+    // Use normalize(true) with these tests to change the date by 1 day.
+    private DateTest[] dayTests = {
+            // The month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11
+
+            // Nov 4, 12am + 0 day = Nov 4, 12am
+            // Nov 5, 12am + 0 day = Nov 5, 12am
+            new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0),
+            new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0),
+
+            // Nov 3, 12am + 1 day = Nov 4, 12am
+            // Nov 4, 12am + 1 day = Nov 5, 12am
+            // Nov 5, 12am + 1 day = Nov 6, 12am
+            new DateTest(2007, 10, 3, 0, 0, 1, 2007, 10, 4, 0, 0),
+            new DateTest(2007, 10, 4, 0, 0, 1, 2007, 10, 5, 0, 0),
+            new DateTest(2007, 10, 5, 0, 0, 1, 2007, 10, 6, 0, 0),
+
+            // Nov 3, 1am + 1 day = Nov 4, 1am
+            // Nov 4, 1am + 1 day = Nov 5, 1am
+            // Nov 5, 1am + 1 day = Nov 6, 1am
+            new DateTest(2007, 10, 3, 1, 0, 1, 2007, 10, 4, 1, 0),
+            new DateTest(2007, 10, 4, 1, 0, 1, 2007, 10, 5, 1, 0),
+            new DateTest(2007, 10, 5, 1, 0, 1, 2007, 10, 6, 1, 0),
+
+            // Nov 3, 2am + 1 day = Nov 4, 2am
+            // Nov 4, 2am + 1 day = Nov 5, 2am
+            // Nov 5, 2am + 1 day = Nov 6, 2am
+            new DateTest(2007, 10, 3, 2, 0, 1, 2007, 10, 4, 2, 0),
+            new DateTest(2007, 10, 4, 2, 0, 1, 2007, 10, 5, 2, 0),
+            new DateTest(2007, 10, 5, 2, 0, 1, 2007, 10, 6, 2, 0),
+    };
+
+    // The "offset" field in "minuteTests" represents minutes.
+    // Use normalize(false) with these tests.
+    private DateTest[] minuteTests = {
+            // The month numbers are 0-relative, so Jan=0, Feb=1,...Dec=11
+
+            // Nov 4, 12am + 0 minutes = Nov 4, 12am
+            // Nov 5, 12am + 0 minutes = Nov 5, 12am
+            new DateTest(2007, 10, 4, 0, 0, 0, 2007, 10, 4, 0, 0),
+            new DateTest(2007, 10, 5, 0, 0, 0, 2007, 10, 5, 0, 0),
+
+            // Nov 3, 12am + 60 minutes = Nov 3, 1am
+            // Nov 4, 12am + 60 minutes = Nov 4, 1am
+            // Nov 5, 12am + 60 minutes = Nov 5, 1am
+            new DateTest(2007, 10, 3, 0, 0, 60, 2007, 10, 3, 1, 0),
+            new DateTest(2007, 10, 4, 0, 0, 60, 2007, 10, 4, 1, 0),
+            new DateTest(2007, 10, 5, 0, 0, 60, 2007, 10, 5, 1, 0),
+
+            // Nov 3, 1am + 60 minutes = Nov 3, 2am
+            // Nov 4, 1am (PDT) + 30 minutes = Nov 4, 1:30am (PDT)
+            // Nov 4, 1am (PDT) + 60 minutes = Nov 4, 1am (PST)
+            new DateTest(2007, 10, 3, 1, 0, 60, 2007, 10, 3, 2, 0),
+            new DateTest(2007, 10, 4, 1, 0, 1, 30, 2007, 10, 4, 1, 30, 1),
+            new DateTest(2007, 10, 4, 1, 0, 1, 60, 2007, 10, 4, 1, 0, 0),
+
+            // Nov 4, 1:30am (PDT) + 15 minutes = Nov 4, 1:45am (PDT)
+            // Nov 4, 1:30am (PDT) + 30 minutes = Nov 4, 1:00am (PST)
+            // Nov 4, 1:30am (PDT) + 60 minutes = Nov 4, 1:30am (PST)
+            new DateTest(2007, 10, 4, 1, 30, 1, 15, 2007, 10, 4, 1, 45, 1),
+            new DateTest(2007, 10, 4, 1, 30, 1, 30, 2007, 10, 4, 1, 0, 0),
+            new DateTest(2007, 10, 4, 1, 30, 1, 60, 2007, 10, 4, 1, 30, 0),
+
+            // Nov 4, 1:30am (PST) + 15 minutes = Nov 4, 1:45am (PST)
+            // Nov 4, 1:30am (PST) + 30 minutes = Nov 4, 2:00am (PST)
+            // Nov 5, 1am + 60 minutes = Nov 5, 2am
+            new DateTest(2007, 10, 4, 1, 30, 0, 15, 2007, 10, 4, 1, 45, 0),
+            new DateTest(2007, 10, 4, 1, 30, 0, 30, 2007, 10, 4, 2, 0, 0),
+            new DateTest(2007, 10, 5, 1, 0, 60, 2007, 10, 5, 2, 0),
+
+            // Nov 3, 2am + 60 minutes = Nov 3, 3am
+            // Nov 4, 2am + 30 minutes = Nov 4, 2:30am
+            // Nov 4, 2am + 60 minutes = Nov 4, 3am
+            // Nov 5, 2am + 60 minutes = Nov 5, 3am
+            new DateTest(2007, 10, 3, 2, 0, 60, 2007, 10, 3, 3, 0),
+            new DateTest(2007, 10, 4, 2, 0, 30, 2007, 10, 4, 2, 30),
+            new DateTest(2007, 10, 4, 2, 0, 60, 2007, 10, 4, 3, 0),
+            new DateTest(2007, 10, 5, 2, 0, 60, 2007, 10, 5, 3, 0),
+    };
+
+    @SmallTest
+    public void testNormalize1() throws Exception {
+        Time local = new Time("America/Los_Angeles");
+
+        int len = dayTests.length;
+        for (int index = 0; index < len; index++) {
+            DateTest test = dayTests[index];
+            local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+            // call normalize() to make sure that isDst is set
+            local.normalize(false /* use isDst */);
+            local.monthDay += test.offset;
+            local.normalize(true /* ignore isDst */);
+            if (local.year != test.year2 || local.month != test.month2
+                    || local.monthDay != test.day2 || local.hour != test.hour2
+                    || local.minute != test.minute2) {
+                String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
+                        test.year2, test.month2, test.day2, test.hour2, test.minute2);
+                String actualTime = String.format("%d-%02d-%02d %02d:%02d",
+                        local.year, local.month, local.monthDay, local.hour, local.minute);
+                throw new RuntimeException(
+                        "day test index " + index + ", normalize(): expected local " + expectedTime
+                                + " got: " + actualTime);
+            }
+
+            local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+            // call normalize() to make sure that isDst is set
+            local.normalize(false /* use isDst */);
+            local.monthDay += test.offset;
+            long millis = local.toMillis(true /* ignore isDst */);
+            local.set(millis);
+            if (local.year != test.year2 || local.month != test.month2
+                    || local.monthDay != test.day2 || local.hour != test.hour2
+                    || local.minute != test.minute2) {
+                String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
+                        test.year2, test.month2, test.day2, test.hour2, test.minute2);
+                String actualTime = String.format("%d-%02d-%02d %02d:%02d",
+                        local.year, local.month, local.monthDay, local.hour, local.minute);
+                throw new RuntimeException(
+                        "day test index " + index + ", toMillis(): expected local " + expectedTime
+                                + " got: " + actualTime);
+            }
+        }
+
+        len = minuteTests.length;
+        for (int index = 0; index < len; index++) {
+            DateTest test = minuteTests[index];
+            local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+            local.isDst = test.dst1;
+            // call normalize() to make sure that isDst is set
+            local.normalize(false /* use isDst */);
+            if (test.dst2 == -1) test.dst2 = local.isDst;
+            local.minute += test.offset;
+            local.normalize(false /* use isDst */);
+            if (local.year != test.year2 || local.month != test.month2
+                    || local.monthDay != test.day2 || local.hour != test.hour2
+                    || local.minute != test.minute2 || local.isDst != test.dst2) {
+                String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+                        test.year2, test.month2, test.day2, test.hour2, test.minute2,
+                        test.dst2);
+                String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+                        local.year, local.month, local.monthDay, local.hour, local.minute,
+                        local.isDst);
+                throw new RuntimeException(
+                        "minute test index " + index + ", normalize(): expected local " + expectedTime
+                                + " got: " + actualTime);
+            }
+
+            local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
+            local.isDst = test.dst1;
+            // call normalize() to make sure that isDst is set
+            local.normalize(false /* use isDst */);
+            if (test.dst2 == -1) test.dst2 = local.isDst;
+            local.minute += test.offset;
+            long millis = local.toMillis(false /* use isDst */);
+            local.set(millis);
+            if (local.year != test.year2 || local.month != test.month2
+                    || local.monthDay != test.day2 || local.hour != test.hour2
+                    || local.minute != test.minute2 || local.isDst != test.dst2) {
+                String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+                        test.year2, test.month2, test.day2, test.hour2, test.minute2,
+                        test.dst2);
+                String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
+                        local.year, local.month, local.monthDay, local.hour, local.minute,
+                        local.isDst);
+                throw new RuntimeException(
+                        "minute test index " + index + ", toMillis(): expected local " + expectedTime
+                                + " got: " + actualTime);
+            }
+        }
+    }
+
+    @SmallTest
+    public void testSwitchTimezone0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.parse("20061005T120000");
+        t.switchTimezone("America/Los_Angeles");
+        // System.out.println("got: " + t);
+    }
+
+    @SmallTest
+    public void testCtor0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        assertEquals(Time.TIMEZONE_UTC, t.timezone);
+    }
+
+    @SmallTest
+    public void testGetActualMaximum0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        int r = t.getActualMaximum(Time.SECOND);
+        // System.out.println("r=" + r);
+    }
+
+    @SmallTest
+    public void testClear0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.clear(Time.TIMEZONE_UTC);
+    }
+
+    @SmallTest
+    public void testCompare0() throws Exception {
+        Time a = new Time(Time.TIMEZONE_UTC);
+        Time b = new Time("America/Los_Angeles");
+        int r = Time.compare(a, b);
+        // System.out.println("r=" + r);
+    }
+
+    @SmallTest
+    public void testFormat0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        String r = t.format("%Y%m%dT%H%M%S");
+        // System.out.println("r='" + r + "'");
+    }
+
+    @SmallTest
+    public void testToString0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        String r = t.toString();
+        // System.out.println("r='" + r + "'");
+    }
+
+    @SmallTest
+    public void testGetCurrentTimezone0() throws Exception {
+        String r = Time.getCurrentTimezone();
+        // System.out.println("r='" + r + "'");
+    }
+
+    @SmallTest
+    public void testSetToNow0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.setToNow();
+        // System.out.println("t=" + t);
+    }
+
+    @SmallTest
+    public void testMillis0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.set(0, 0, 0, 1, 1, 2006);
+        long r = t.toMillis(true /* ignore isDst */);
+        // System.out.println("r=" + r);
+        t.set(1, 0, 0, 1, 1, 2006);
+        r = t.toMillis(true /* ignore isDst */);
+        // System.out.println("r=" + r);
+    }
+
+    @SmallTest
+    public void testMillis1() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.set(1, 0, 0, 1, 0, 1970);
+        long r = t.toMillis(true /* ignore isDst */);
+        // System.out.println("r=" + r);
+    }
+
+    @SmallTest
+    public void testParse0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.parse("12345678T901234");
+        // System.out.println("t=" + t);
+    }
+
+    @SmallTest
+    public void testParse33390() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+
+        t.parse3339("1980-05-23");
+        if (!t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23) {
+            fail("Did not parse all-day date correctly");
+        }
+
+        t.parse3339("1980-05-23T09:50:50");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 9 || t.minute != 50 || t.second != 50 ||
+                t.gmtoff != 0) {
+            fail("Did not parse timezone-offset-less date correctly");
+        }
+
+        t.parse3339("1980-05-23T09:50:50Z");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 9 || t.minute != 50 || t.second != 50 ||
+                t.gmtoff != 0) {
+            fail("Did not parse UTC date correctly");
+        }
+
+        t.parse3339("1980-05-23T09:50:50.0Z");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 9 || t.minute != 50 || t.second != 50 ||
+                t.gmtoff != 0) {
+            fail("Did not parse UTC date correctly");
+        }
+
+        t.parse3339("1980-05-23T09:50:50.12Z");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 9 || t.minute != 50 || t.second != 50 ||
+                t.gmtoff != 0) {
+            fail("Did not parse UTC date correctly");
+        }
+
+        t.parse3339("1980-05-23T09:50:50.123Z");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 9 || t.minute != 50 || t.second != 50 ||
+                t.gmtoff != 0) {
+            fail("Did not parse UTC date correctly");
+        }
+
+        // The time should be normalized to UTC
+        t.parse3339("1980-05-23T09:50:50-01:05");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 10 || t.minute != 55 || t.second != 50 ||
+                t.gmtoff != 0) {
+            fail("Did not parse timezone-offset date correctly");
+        }
+
+        // The time should be normalized to UTC
+        t.parse3339("1980-05-23T09:50:50.123-01:05");
+        if (t.allDay || t.year != 1980 || t.month != 04 || t.monthDay != 23 ||
+                t.hour != 10 || t.minute != 55 || t.second != 50 ||
+                t.gmtoff != 0) {
+            fail("Did not parse timezone-offset date correctly");
+        }
+
+        try {
+            t.parse3339("1980");
+            fail("Did not throw error on truncated input length");
+        } catch (TimeFormatException e) {
+            // Successful
+        }
+
+        try {
+            t.parse3339("1980-05-23T09:50:50.123+");
+            fail("Did not throw error on truncated timezone offset");
+        } catch (TimeFormatException e1) {
+            // Successful
+        }
+
+        try {
+            t.parse3339("1980-05-23T09:50:50.123+05:0");
+            fail("Did not throw error on truncated timezone offset");
+        } catch (TimeFormatException e1) {
+            // Successful
+        }
+    }
+
+    @SmallTest
+    public void testSet0() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.set(1000L);
+        // System.out.println("t.year=" + t.year);
+        // System.out.println("t=" + t);
+        t.set(2000L);
+        // System.out.println("t=" + t);
+        t.set(1000L * 60);
+        // System.out.println("t=" + t);
+        t.set((1000L * 60 * 60 * 24) + 1000L);
+        // System.out.println("t=" + t);
+    }
+
+    @SmallTest
+    public void testSet1() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.set(1, 2, 3, 4, 5, 6);
+        // System.out.println("t=" + t);
+    }
+    
+    // Timezones that cover the world.  Some GMT offsets occur more than
+    // once in case some cities decide to change their GMT offset.
+    private static final String[] mTimeZones = {
+        "Pacific/Kiritimati",
+        "Pacific/Enderbury",
+        "Pacific/Fiji",
+        "Antarctica/South_Pole",
+        "Pacific/Norfolk",
+        "Pacific/Ponape",
+        "Asia/Magadan",
+        "Australia/Lord_Howe",
+        "Australia/Sydney",
+        "Australia/Adelaide",
+        "Asia/Tokyo",
+        "Asia/Seoul",
+        "Asia/Taipei",
+        "Asia/Singapore",
+        "Asia/Hong_Kong",
+        "Asia/Saigon",
+        "Asia/Bangkok",
+        "Indian/Cocos",
+        "Asia/Rangoon",
+        "Asia/Omsk",
+        "Antarctica/Mawson",
+        "Asia/Colombo",
+        "Asia/Calcutta",
+        "Asia/Oral",
+        "Asia/Kabul",
+        "Asia/Dubai",
+        "Asia/Tehran",
+        "Europe/Moscow",
+        "Asia/Baghdad",
+        "Africa/Mogadishu",
+        "Europe/Athens",
+        "Africa/Cairo",
+        "Europe/Rome",
+        "Europe/Berlin",
+        "Europe/Amsterdam",
+        "Africa/Tunis",
+        "Europe/London",
+        "Europe/Dublin",
+        "Atlantic/St_Helena",
+        "Africa/Monrovia",
+        "Africa/Accra",
+        "Atlantic/Azores",
+        "Atlantic/South_Georgia",
+        "America/Noronha",
+        "America/Sao_Paulo",
+        "America/Cayenne",
+        "America/St_Johns",
+        "America/Puerto_Rico",
+        "America/Aruba",
+        "America/New_York",
+        "America/Chicago",
+        "America/Denver",
+        "America/Los_Angeles",
+        "America/Anchorage",
+        "Pacific/Marquesas",
+        "America/Adak",
+        "Pacific/Honolulu",
+        "Pacific/Midway",
+    };
+    
+    @Suppress
+    public void disableTestGetJulianDay() throws Exception {
+        Time time = new Time();
+
+        // For each day of the year, and for each timezone, get the Julian
+        // day for 12am and then check that if we change the time we get the
+        // same Julian day.
+        for (int monthDay = 1; monthDay <= 366; monthDay++) {
+            for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) {
+                // We leave the "month" as zero because we are changing the
+                // "monthDay" from 1 to 366.  The call to normalize() will
+                // then change the "month" (but we don't really care).
+                time.set(0, 0, 0, monthDay, 0, 2008);
+                time.timezone = mTimeZones[zoneIndex];
+                long millis = time.normalize(true);
+                if (zoneIndex == 0) {
+                    Log.i("TimeTest", time.format("%B %d, %Y"));
+                }
+                
+                // This is the Julian day for 12am for this day of the year
+                int julianDay = Time.getJulianDay(millis, time.gmtoff);
+
+                // Change the time during the day and check that we get the same
+                // Julian day.
+                for (int hour = 0; hour < 24; hour++) {
+                    for (int minute = 0; minute < 60; minute += 15) {
+                        time.set(0, minute, hour, monthDay, 0, 2008);
+                        millis = time.normalize(true);
+                        int day = Time.getJulianDay(millis, time.gmtoff);
+                        if (day != julianDay) {
+                            Log.e("TimeTest", "Julian day: " + day + " at time "
+                                    + time.hour + ":" + time.minute
+                                    + " != today's Julian day: " + julianDay
+                                    + " timezone: " + time.timezone);
+                        }
+                        assertEquals(day, julianDay);
+                    }
+                }
+            }
+        }
+    }
+    
+    @Suppress
+    public void disableTestSetJulianDay() throws Exception {
+        Time time = new Time();
+        
+        // For each day of the year in 2008, and for each timezone,
+        // test that we can set the Julian day correctly.
+        for (int monthDay = 1; monthDay <= 366; monthDay++) {
+            for (int zoneIndex = 0; zoneIndex < mTimeZones.length; zoneIndex++) {
+                // We leave the "month" as zero because we are changing the
+                // "monthDay" from 1 to 366.  The call to normalize() will
+                // then change the "month" (but we don't really care).
+                time.set(0, 0, 0, monthDay, 0, 2008);
+                time.timezone = mTimeZones[zoneIndex];
+                long millis = time.normalize(true);
+                if (zoneIndex == 0) {
+                    Log.i("TimeTest", time.format("%B %d, %Y"));
+                }
+                int julianDay = Time.getJulianDay(millis, time.gmtoff);
+                
+                time.setJulianDay(julianDay);
+                
+                // Some places change daylight saving time at 12am and so there
+                // is no 12am on some days in some timezones.  In those cases,
+                // the time is set to 1am.
+                // Examples: Africa/Cairo on April 25, 2008
+                //  America/Sao_Paulo on October 12, 2008
+                //  Atlantic/Azores on March 30, 2008
+                assertTrue(time.hour == 0 || time.hour == 1);
+                assertEquals(0, time.minute);
+                assertEquals(0, time.second);
+
+                millis = time.toMillis(false);
+                int day = Time.getJulianDay(millis, time.gmtoff);
+                if (day != julianDay) {
+                    Log.i("TimeTest", "Error: gmtoff " + (time.gmtoff / 3600.0)
+                            + " day " + julianDay
+                            + " millis " + millis
+                            + " " + time.format("%B %d, %Y") + " " + time.timezone);
+                }
+                assertEquals(day, julianDay);
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/util/LogTest.java b/core/tests/coretests/src/android/util/LogTest.java
new file mode 100644
index 0000000..41947d7
--- /dev/null
+++ b/core/tests/coretests/src/android/util/LogTest.java
@@ -0,0 +1,152 @@
+package android.util;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import android.os.SystemProperties;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.Log;
+
+//This is an empty TestCase.
+@Suppress
+public class LogTest extends TestCase {
+    private static final String PROPERTY_TAG = "log.tag.LogTest";
+    private static final String LOG_TAG = "LogTest";
+
+
+    // TODO: remove this test once we uncomment out the following test.
+    public void testLogTestDummy() {
+      return;
+    }
+
+
+    /* TODO: This test is commented out because we will not be able to set properities. Fix the test.
+    public void testIsLoggable() {
+        // First clear any SystemProperty setting for our test key.
+        SystemProperties.set(PROPERTY_TAG, null);
+        
+        String value = SystemProperties.get(PROPERTY_TAG);
+        Assert.assertTrue(value == null || value.length() == 0);
+        
+        // Check to make sure that all levels expect for INFO, WARN, ERROR, and ASSERT are loggable. 
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+        
+        // Set the log level to be VERBOSE for this tag.
+        SystemProperties.set(PROPERTY_TAG, "VERBOSE");
+        
+        // Test to make sure all log levels >= VERBOSE are loggable.
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+        
+        // Set the log level to be DEBUG for this tag.
+        SystemProperties.set(PROPERTY_TAG, "DEBUG");
+        
+        // Test to make sure all log levels >= DEBUG are loggable.
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+        
+        // Set the log level to be INFO for this tag.
+        SystemProperties.set(PROPERTY_TAG, "INFO");
+        
+        // Test to make sure all log levels >= INFO are loggable.
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+        
+        // Set the log level to be WARN for this tag.
+        SystemProperties.set(PROPERTY_TAG, "WARN");
+        
+        // Test to make sure all log levels >= WARN are loggable.
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+        
+        // Set the log level to be ERROR for this tag.
+        SystemProperties.set(PROPERTY_TAG, "ERROR");
+        
+        // Test to make sure all log levels >= ERROR are loggable.
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+        
+        // Set the log level to be ASSERT for this tag.
+        SystemProperties.set(PROPERTY_TAG, "ASSERT");
+        
+        // Test to make sure all log levels >= ASSERT are loggable.
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertTrue(Log.isLoggable(LOG_TAG, Log.ASSERT));
+        
+        // Set the log level to be SUPPRESS for this tag.
+        SystemProperties.set(PROPERTY_TAG, "SUPPRESS");
+        
+        // Test to make sure all log levels >= ASSERT are loggable.
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.VERBOSE));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.DEBUG));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.INFO));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.WARN));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ERROR));
+        Assert.assertFalse(Log.isLoggable(LOG_TAG, Log.ASSERT));
+    }
+    */
+    
+    public static class PerformanceTest extends TestCase implements PerformanceTestCase {
+        private static final int ITERATIONS = 1000;
+
+        @Override
+        public void setUp() {
+            SystemProperties.set(LOG_TAG, "VERBOSE");
+        }
+        
+        public boolean isPerformanceOnly() {
+            return true;
+        }
+        
+        public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
+            intermediates.setInternalIterations(ITERATIONS * 10);
+            return 0;
+        }
+
+        public void testIsLoggable() {
+            boolean canLog = false;
+            for (int i = ITERATIONS - 1; i >= 0; i--) {
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+                canLog = Log.isLoggable(LOG_TAG, Log.VERBOSE);
+            }
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/util/TimeUtilsTest.java b/core/tests/coretests/src/android/util/TimeUtilsTest.java
new file mode 100644
index 0000000..65a6078
--- /dev/null
+++ b/core/tests/coretests/src/android/util/TimeUtilsTest.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.util;
+
+import junit.framework.TestCase;
+
+import android.util.TimeUtils;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * TimeUtilsTest tests the time zone guesser.
+ */
+public class TimeUtilsTest extends TestCase {
+    public void testMainstream() throws Exception {
+        String[] mainstream = new String[] {
+            "America/New_York", // Eastern
+            "America/Chicago", // Central
+            "America/Denver", // Mountain
+            "America/Los_Angeles", // Pacific
+            "America/Anchorage", // Alaska
+            "Pacific/Honolulu", // Hawaii, no DST
+        };
+
+        for (String name : mainstream) {
+            TimeZone tz = TimeZone.getTimeZone(name);
+            Calendar c = Calendar.getInstance(tz);
+            TimeZone guess;
+
+            c.set(2008, Calendar.OCTOBER, 20, 12, 00, 00);
+            guess = guess(c, "us");
+            assertEquals(name, guess.getID());
+
+            c.set(2009, Calendar.JANUARY, 20, 12, 00, 00);
+            guess = guess(c, "us");
+            assertEquals(name, guess.getID());
+        }
+    }
+
+    public void testWeird() throws Exception {
+        String[] weird = new String[] {
+            "America/Phoenix", // Mountain, no DST
+            "America/Adak", // Same as Hawaii, but with DST
+        };
+
+        for (String name : weird) {
+            TimeZone tz = TimeZone.getTimeZone(name);
+            Calendar c = Calendar.getInstance(tz);
+            TimeZone guess;
+
+            c.set(2008, Calendar.OCTOBER, 20, 12, 00, 00);
+            guess = guess(c, "us");
+            assertEquals(name, guess.getID());
+        }
+    }
+
+    public void testOld() throws Exception {
+        String[] old = new String[] {
+            "America/Indiana/Indianapolis", // Eastern, formerly no DST
+        };
+
+        for (String name : old) {
+            TimeZone tz = TimeZone.getTimeZone(name);
+            Calendar c = Calendar.getInstance(tz);
+            TimeZone guess;
+
+            c.set(2005, Calendar.OCTOBER, 20, 12, 00, 00);
+            guess = guess(c, "us");
+            assertEquals(name, guess.getID());
+        }
+    }
+
+    public void testWorld() throws Exception {
+        String[] world = new String[] {
+            "ad", "Europe/Andorra",
+            "ae", "Asia/Dubai",
+            "af", "Asia/Kabul",
+            "ag", "America/Antigua",
+            "ai", "America/Anguilla",
+            "al", "Europe/Tirane",
+            "am", "Asia/Yerevan",
+            "an", "America/Curacao",
+            "ao", "Africa/Luanda",
+            "aq", "Antarctica/McMurdo",
+            "aq", "Antarctica/DumontDUrville",
+            "aq", "Antarctica/Casey",
+            "aq", "Antarctica/Davis",
+            "aq", "Antarctica/Mawson",
+            "aq", "Antarctica/Syowa",
+            "aq", "Antarctica/Rothera",
+            "aq", "Antarctica/Palmer",
+            "ar", "America/Argentina/Buenos_Aires",
+            "as", "Pacific/Pago_Pago",
+            "at", "Europe/Vienna",
+            "au", "Australia/Sydney",
+            "au", "Australia/Adelaide",
+            "au", "Australia/Perth",
+            "au", "Australia/Eucla",
+            "aw", "America/Aruba",
+            "ax", "Europe/Mariehamn",
+            "az", "Asia/Baku",
+            "ba", "Europe/Sarajevo",
+            "bb", "America/Barbados",
+            "bd", "Asia/Dhaka",
+            "be", "Europe/Brussels",
+            "bf", "Africa/Ouagadougou",
+            "bg", "Europe/Sofia",
+            "bh", "Asia/Bahrain",
+            "bi", "Africa/Bujumbura",
+            "bj", "Africa/Porto-Novo",
+            "bm", "Atlantic/Bermuda",
+            "bn", "Asia/Brunei",
+            "bo", "America/La_Paz",
+            "br", "America/Noronha",
+            "br", "America/Sao_Paulo",
+            "br", "America/Manaus",
+            "bs", "America/Nassau",
+            "bt", "Asia/Thimphu",
+            "bw", "Africa/Gaborone",
+            "by", "Europe/Minsk",
+            "bz", "America/Belize",
+            "ca", "America/St_Johns",
+            "ca", "America/Halifax",
+            "ca", "America/Toronto",
+            "ca", "America/Winnipeg",
+            "ca", "America/Edmonton",
+            "ca", "America/Vancouver",
+            "cc", "Indian/Cocos",
+            "cd", "Africa/Lubumbashi",
+            "cd", "Africa/Kinshasa",
+            "cf", "Africa/Bangui",
+            "cg", "Africa/Brazzaville",
+            "ch", "Europe/Zurich",
+            "ci", "Africa/Abidjan",
+            "ck", "Pacific/Rarotonga",
+            "cl", "America/Santiago",
+            "cl", "Pacific/Easter",
+            "cm", "Africa/Douala",
+            "cn", "Asia/Shanghai",
+            "co", "America/Bogota",
+            "cr", "America/Costa_Rica",
+            "cu", "America/Havana",
+            "cv", "Atlantic/Cape_Verde",
+            "cx", "Indian/Christmas",
+            "cy", "Asia/Nicosia",
+            "cz", "Europe/Prague",
+            "de", "Europe/Berlin",
+            "dj", "Africa/Djibouti",
+            "dk", "Europe/Copenhagen",
+            "dm", "America/Dominica",
+            "do", "America/Santo_Domingo",
+            "dz", "Africa/Algiers",
+            "ec", "America/Guayaquil",
+            "ec", "Pacific/Galapagos",
+            "ee", "Europe/Tallinn",
+            "eg", "Africa/Cairo",
+            "eh", "Africa/El_Aaiun",
+            "er", "Africa/Asmara",
+            "es", "Europe/Madrid",
+            "es", "Atlantic/Canary",
+            "et", "Africa/Addis_Ababa",
+            "fi", "Europe/Helsinki",
+            "fj", "Pacific/Fiji",
+            "fk", "Atlantic/Stanley",
+            "fm", "Pacific/Ponape",
+            "fm", "Pacific/Truk",
+            "fo", "Atlantic/Faroe",
+            "fr", "Europe/Paris",
+            "ga", "Africa/Libreville",
+            "gb", "Europe/London",
+            "gd", "America/Grenada",
+            "ge", "Asia/Tbilisi",
+            "gf", "America/Cayenne",
+            "gg", "Europe/Guernsey",
+            "gh", "Africa/Accra",
+            "gi", "Europe/Gibraltar",
+            "gl", "America/Danmarkshavn",
+            "gl", "America/Scoresbysund",
+            "gl", "America/Godthab",
+            "gl", "America/Thule",
+            "gm", "Africa/Banjul",
+            "gn", "Africa/Conakry",
+            "gp", "America/Guadeloupe",
+            "gq", "Africa/Malabo",
+            "gr", "Europe/Athens",
+            "gs", "Atlantic/South_Georgia",
+            "gt", "America/Guatemala",
+            "gu", "Pacific/Guam",
+            "gw", "Africa/Bissau",
+            "gy", "America/Guyana",
+            "hk", "Asia/Hong_Kong",
+            "hn", "America/Tegucigalpa",
+            "hr", "Europe/Zagreb",
+            "ht", "America/Port-au-Prince",
+            "hu", "Europe/Budapest",
+            "id", "Asia/Jayapura",
+            "id", "Asia/Makassar",
+            "id", "Asia/Jakarta",
+            "ie", "Europe/Dublin",
+            "il", "Asia/Jerusalem",
+            "im", "Europe/Isle_of_Man",
+            "in", "Asia/Calcutta",
+            "io", "Indian/Chagos",
+            "iq", "Asia/Baghdad",
+            "ir", "Asia/Tehran",
+            "is", "Atlantic/Reykjavik",
+            "it", "Europe/Rome",
+            "je", "Europe/Jersey",
+            "jm", "America/Jamaica",
+            "jo", "Asia/Amman",
+            "jp", "Asia/Tokyo",
+            "ke", "Africa/Nairobi",
+            "kg", "Asia/Bishkek",
+            "kh", "Asia/Phnom_Penh",
+            "ki", "Pacific/Kiritimati",
+            "ki", "Pacific/Enderbury",
+            "ki", "Pacific/Tarawa",
+            "km", "Indian/Comoro",
+            "kn", "America/St_Kitts",
+            "kp", "Asia/Pyongyang",
+            "kr", "Asia/Seoul",
+            "kw", "Asia/Kuwait",
+            "ky", "America/Cayman",
+            "kz", "Asia/Almaty",
+            "kz", "Asia/Aqtau",
+            "la", "Asia/Vientiane",
+            "lb", "Asia/Beirut",
+            "lc", "America/St_Lucia",
+            "li", "Europe/Vaduz",
+            "lk", "Asia/Colombo",
+            "lr", "Africa/Monrovia",
+            "ls", "Africa/Maseru",
+            "lt", "Europe/Vilnius",
+            "lu", "Europe/Luxembourg",
+            "lv", "Europe/Riga",
+            "ly", "Africa/Tripoli",
+            "ma", "Africa/Casablanca",
+            "mc", "Europe/Monaco",
+            "md", "Europe/Chisinau",
+            "me", "Europe/Podgorica",
+            "mg", "Indian/Antananarivo",
+            "mh", "Pacific/Majuro",
+            "mk", "Europe/Skopje",
+            "ml", "Africa/Bamako",
+            "mm", "Asia/Rangoon",
+            "mn", "Asia/Choibalsan",
+            "mn", "Asia/Hovd",
+            "mo", "Asia/Macau",
+            "mp", "Pacific/Saipan",
+            "mq", "America/Martinique",
+            "mr", "Africa/Nouakchott",
+            "ms", "America/Montserrat",
+            "mt", "Europe/Malta",
+            "mu", "Indian/Mauritius",
+            "mv", "Indian/Maldives",
+            "mw", "Africa/Blantyre",
+            "mx", "America/Mexico_City",
+            "mx", "America/Chihuahua",
+            "mx", "America/Tijuana",
+            "my", "Asia/Kuala_Lumpur",
+            "mz", "Africa/Maputo",
+            "na", "Africa/Windhoek",
+            "nc", "Pacific/Noumea",
+            "ne", "Africa/Niamey",
+            "nf", "Pacific/Norfolk",
+            "ng", "Africa/Lagos",
+            "ni", "America/Managua",
+            "nl", "Europe/Amsterdam",
+            "no", "Europe/Oslo",
+            "np", "Asia/Katmandu",
+            "nr", "Pacific/Nauru",
+            "nu", "Pacific/Niue",
+            "nz", "Pacific/Auckland",
+            "nz", "Pacific/Chatham",
+            "om", "Asia/Muscat",
+            "pa", "America/Panama",
+            "pe", "America/Lima",
+            "pf", "Pacific/Gambier",
+            "pf", "Pacific/Marquesas",
+            "pf", "Pacific/Tahiti",
+            "pg", "Pacific/Port_Moresby",
+            "ph", "Asia/Manila",
+            "pk", "Asia/Karachi",
+            "pl", "Europe/Warsaw",
+            "pm", "America/Miquelon",
+            "pn", "Pacific/Pitcairn",
+            "pr", "America/Puerto_Rico",
+            "ps", "Asia/Gaza",
+            "pt", "Europe/Lisbon",
+            "pt", "Atlantic/Azores",
+            "pw", "Pacific/Palau",
+            "py", "America/Asuncion",
+            "qa", "Asia/Qatar",
+            "re", "Indian/Reunion",
+            "ro", "Europe/Bucharest",
+            "rs", "Europe/Belgrade",
+            "ru", "Asia/Kamchatka",
+            "ru", "Asia/Magadan",
+            "ru", "Asia/Vladivostok",
+            "ru", "Asia/Yakutsk",
+            "ru", "Asia/Irkutsk",
+            "ru", "Asia/Krasnoyarsk",
+            "ru", "Asia/Novosibirsk",
+            "ru", "Asia/Yekaterinburg",
+            "ru", "Europe/Samara",
+            "ru", "Europe/Moscow",
+            "ru", "Europe/Kaliningrad",
+            "rw", "Africa/Kigali",
+            "sa", "Asia/Riyadh",
+            "sb", "Pacific/Guadalcanal",
+            "sc", "Indian/Mahe",
+            "sd", "Africa/Khartoum",
+            "se", "Europe/Stockholm",
+            "sg", "Asia/Singapore",
+            "sh", "Atlantic/St_Helena",
+            "si", "Europe/Ljubljana",
+            "sj", "Arctic/Longyearbyen",
+            "sk", "Europe/Bratislava",
+            "sl", "Africa/Freetown",
+            "sm", "Europe/San_Marino",
+            "sn", "Africa/Dakar",
+            "so", "Africa/Mogadishu",
+            "sr", "America/Paramaribo",
+            "st", "Africa/Sao_Tome",
+            "sv", "America/El_Salvador",
+            "sy", "Asia/Damascus",
+            "sz", "Africa/Mbabane",
+            "tc", "America/Grand_Turk",
+            "td", "Africa/Ndjamena",
+            "tf", "Indian/Kerguelen",
+            "tg", "Africa/Lome",
+            "th", "Asia/Bangkok",
+            "tj", "Asia/Dushanbe",
+            "tk", "Pacific/Fakaofo",
+            "tl", "Asia/Dili",
+            "tm", "Asia/Ashgabat",
+            "tn", "Africa/Tunis",
+            "to", "Pacific/Tongatapu",
+            "tr", "Europe/Istanbul",
+            "tt", "America/Port_of_Spain",
+            "tv", "Pacific/Funafuti",
+            "tw", "Asia/Taipei",
+            "tz", "Africa/Dar_es_Salaam",
+            "ua", "Europe/Kiev",
+            "ug", "Africa/Kampala",
+            "um", "Pacific/Wake",
+            "um", "Pacific/Johnston",
+            "um", "Pacific/Midway",
+            "us", "America/New_York",
+            "us", "America/Chicago",
+            "us", "America/Denver",
+            "us", "America/Los_Angeles",
+            "us", "America/Anchorage",
+            "us", "Pacific/Honolulu",
+            "uy", "America/Montevideo",
+            "uz", "Asia/Tashkent",
+            "va", "Europe/Vatican",
+            "vc", "America/St_Vincent",
+            "ve", "America/Caracas",
+            "vg", "America/Tortola",
+            "vi", "America/St_Thomas",
+            "vn", "Asia/Saigon",
+            "vu", "Pacific/Efate",
+            "wf", "Pacific/Wallis",
+            "ws", "Pacific/Apia",
+            "ye", "Asia/Aden",
+            "yt", "Indian/Mayotte",
+            "za", "Africa/Johannesburg",
+            "zm", "Africa/Lusaka",
+            "zw", "Africa/Harare",
+        };
+
+        for (int i = 0; i < world.length; i += 2) {
+            String country = world[i];
+            String name = world[i + 1];
+
+            TimeZone tz = TimeZone.getTimeZone(name);
+            Calendar c = Calendar.getInstance(tz);
+            TimeZone guess;
+
+            c.set(2009, Calendar.JULY, 20, 12, 00, 00);
+            guess = guess(c, country);
+            assertEquals(name, guess.getID());
+
+            c.set(2009, Calendar.JANUARY, 20, 12, 00, 00);
+            guess = guess(c, country);
+            assertEquals(name, guess.getID());
+        }
+    }
+
+    public void testWorldWeird() throws Exception {
+        String[] world = new String[] {
+            // Distinguisable from Sydney only when DST not in effect
+            "au", "Australia/Lord_Howe",
+        };
+
+        for (int i = 0; i < world.length; i += 2) {
+            String country = world[i];
+            String name = world[i + 1];
+
+            TimeZone tz = TimeZone.getTimeZone(name);
+            Calendar c = Calendar.getInstance(tz);
+            TimeZone guess;
+
+            c.set(2009, Calendar.JULY, 20, 12, 00, 00);
+            guess = guess(c, country);
+            assertEquals(name, guess.getID());
+        }
+    }
+
+    private static TimeZone guess(Calendar c, String country) {
+        return TimeUtils.getTimeZone(c.get(c.ZONE_OFFSET) + c.get(c.DST_OFFSET),
+                                     c.get(c.DST_OFFSET) != 0,
+                                     c.getTimeInMillis(),
+                                     country);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/CreateViewTest.java b/core/tests/coretests/src/android/view/CreateViewTest.java
new file mode 100644
index 0000000..16656f6
--- /dev/null
+++ b/core/tests/coretests/src/android/view/CreateViewTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class CreateViewTest extends AndroidTestCase implements PerformanceTestCase {
+
+    public boolean isPerformanceOnly() {
+        return false;
+    }
+
+    public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
+        return 0;
+    }
+
+    @SmallTest
+    public void testLayout1() throws Exception {
+        new CreateViewTest.ViewOne(mContext);
+    }
+
+    @SmallTest
+    public void testLayout2() throws Exception {
+        LinearLayout vert = new LinearLayout(mContext);
+        vert.addView(new CreateViewTest.ViewOne(mContext),
+                new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+    }
+
+    @SmallTest
+    public void testLayout3() throws Exception {
+        LinearLayout vert = new LinearLayout(mContext);
+
+        ViewOne one = new ViewOne(mContext);
+        vert.addView(one, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+        ViewOne two = new ViewOne(mContext);
+        vert.addView(two, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+        ViewOne three = new ViewOne(mContext);
+        vert.addView(three, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+        ViewOne four = new ViewOne(mContext);
+        vert.addView(four, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+        ViewOne five = new ViewOne(mContext);
+        vert.addView(five, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+
+        ViewOne six = new ViewOne(mContext);
+        vert.addView(six, new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT, 0));
+    }
+
+    @SmallTest
+    public void testLayout4() throws Exception {
+        TextView text = new TextView(mContext);
+        text.setText("S");
+    }
+
+    @SmallTest
+    public void testLayout5() throws Exception {
+        TextView text = new TextView(mContext);
+        text.setText("S");
+
+        LinearLayout vert = new LinearLayout(mContext);
+        vert.addView(text, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+    }
+
+    @SmallTest
+    public void testLayout6() throws Exception {
+        LinearLayout vert = new LinearLayout(mContext);
+
+        TextView one = new TextView(mContext);
+        one.setText("S");
+        vert.addView(one, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+        TextView two = new TextView(mContext);
+        two.setText("M");
+        vert.addView(two, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+        TextView three = new TextView(mContext);
+        three.setText("T");
+        vert.addView(three, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+        TextView four = new TextView(mContext);
+        four.setText("W");
+        vert.addView(four, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+        TextView five = new TextView(mContext);
+        five.setText("H");
+        vert.addView(five, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+
+        TextView six = new TextView(mContext);
+        six.setText("F");
+        vert.addView(six, new LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT, 0));
+    }
+
+    public static class ViewOne extends View {
+        public ViewOne(Context context) {
+            super(context);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/InflateTest.java b/core/tests/coretests/src/android/view/InflateTest.java
new file mode 100644
index 0000000..cb4f8e2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/InflateTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.PerformanceTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import com.android.frameworks.coretests.R;
+
+import java.util.Map;
+
+public class InflateTest extends AndroidTestCase implements PerformanceTestCase {
+    private LayoutInflater mInflater;
+    private Resources mResources;
+    private View mView;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mInflater = LayoutInflater.from(mContext);
+        mResources = mContext.getResources();
+
+        // to try to make things consistent, before doing timing
+        // do an initial instantiation of the layout and then clear
+        // out the layout cache.
+//            mInflater.inflate(mResId, null, null);
+//            mResources.flushLayoutCache();
+    }
+
+    public int startPerformance(PerformanceTestCase.Intermediates intermediates) {
+        return 0;
+    }
+
+    public boolean isPerformanceOnly() {
+        return false;
+    }
+
+    public void inflateTest(int resourceId) {
+        mView = mInflater.inflate(resourceId, null);
+        mResources.flushLayoutCache();
+    }
+
+    public void inflateCachedTest(int resourceId) {
+        // Make sure this layout is in the cache.
+        mInflater.inflate(resourceId, null);
+
+        mInflater.inflate(resourceId, null);
+    }
+
+    @SmallTest
+    public void testLayout1() throws Exception {
+        inflateTest(R.layout.layout_one);
+    }
+
+    @SmallTest
+    public void testLayout2() throws Exception {
+        inflateTest(R.layout.layout_two);
+    }
+
+    @SmallTest
+    public void testLayout3() throws Exception {
+        inflateTest(R.layout.layout_three);
+    }
+
+    @SmallTest
+    public void testLayout4() throws Exception {
+        inflateTest(R.layout.layout_four);
+    }
+
+    @SmallTest
+    public void testLayout5() throws Exception {
+        inflateTest(R.layout.layout_five);
+    }
+
+    @SmallTest
+    public void testLayout6() throws Exception {
+        inflateTest(R.layout.layout_six);
+    }
+
+    @SmallTest
+    public void testCachedLayout1() throws Exception {
+        inflateCachedTest(R.layout.layout_one);
+    }
+
+    @SmallTest
+    public void testCachedLayout2() throws Exception {
+        inflateCachedTest(R.layout.layout_two);
+    }
+
+    @SmallTest
+    public void testCachedLayout3() throws Exception {
+        inflateCachedTest(R.layout.layout_three);
+    }
+
+    @SmallTest
+    public void testCachedLayout4() throws Exception {
+        inflateCachedTest(R.layout.layout_four);
+    }
+
+    @SmallTest
+    public void testCachedLayout5() throws Exception {
+        inflateCachedTest(R.layout.layout_five);
+    }
+
+    @SmallTest
+    public void testCachedLayout6() throws Exception {
+        inflateCachedTest(R.layout.layout_six);
+    }
+
+//    public void testLayoutTag() throws Exception {
+//        public void setUp
+//        (Context
+//        context){
+//        setUp(context, R.layout.layout_tag);
+//    }
+//        public void run
+//        ()
+//        {
+//            super.run();
+//            if (!"MyTag".equals(mView.getTag())) {
+//                throw new RuntimeException("Incorrect tag: " + mView.getTag());
+//            }
+//        }
+//    }
+
+    public static class ViewOne extends View {
+        public ViewOne(Context context) {
+            super(context);
+        }
+
+        public ViewOne(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/view/MenuTest.java b/core/tests/coretests/src/android/view/MenuTest.java
new file mode 100644
index 0000000..e8a8438
--- /dev/null
+++ b/core/tests/coretests/src/android/view/MenuTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view;
+
+import com.android.internal.view.menu.MenuBuilder;
+
+import junit.framework.Assert;
+
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+
+import com.android.frameworks.coretests.R;
+
+public class MenuTest extends AndroidTestCase {
+
+    private MenuBuilder mMenu;
+
+    public void setUp() throws Exception {
+        super.setUp();
+        mMenu = new MenuBuilder(super.getContext());
+    }
+
+    @SmallTest
+    public void testItemId() {
+        final int id = 512;
+        final MenuItem item = mMenu.add(0, id, 0, "test");
+        
+        Assert.assertEquals(id, item.getItemId());
+        Assert.assertEquals(item, mMenu.findItem(id));
+        Assert.assertEquals(0, mMenu.findItemIndex(id));
+    }
+    
+    @SmallTest
+    public void testGroupId() {
+        final int groupId = 541;
+        final int item1Index = 1;
+        final int item2Index = 3;
+        
+        mMenu.add(0, 0, item1Index - 1, "ignore");
+        final MenuItem item = mMenu.add(groupId, 0, item1Index, "test");
+        mMenu.add(0, 0, item2Index - 1, "ignore");
+        final MenuItem item2 = mMenu.add(groupId, 0, item2Index, "test2");
+        
+        Assert.assertEquals(groupId, item.getGroupId());
+        Assert.assertEquals(groupId, item2.getGroupId());
+        Assert.assertEquals(item1Index, mMenu.findGroupIndex(groupId));
+        Assert.assertEquals(item2Index, mMenu.findGroupIndex(groupId, item1Index + 1));
+    }
+    
+    @SmallTest
+    public void testGroup() {
+        // This test does the following
+        //  1. Create a grouped item in the menu
+        //  2. Check that findGroupIndex() finds the grouped item.
+        //  3. Check that findGroupIndex() doesn't find a non-existent group.
+
+        final int GROUP_ONE = Menu.FIRST;
+        final int GROUP_TWO = Menu.FIRST + 1;
+
+        mMenu.add(GROUP_ONE, 0, 0, "Menu text");
+        Assert.assertEquals(mMenu.findGroupIndex(GROUP_ONE), 0);
+        Assert.assertEquals(mMenu.findGroupIndex(GROUP_TWO), -1);
+        //TODO: expand this test case to do multiple groups,
+        //adding and removing, hiding and showing, etc.
+    }
+
+    @SmallTest
+    public void testIsShortcutWithAlpha() throws Exception {
+        mMenu.setQwertyMode(true);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+        Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+                                              makeKeyEvent(KeyEvent.KEYCODE_A,  0)));
+        Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_B,
+                                               makeKeyEvent(KeyEvent.KEYCODE_B,  0)));
+    }
+
+    @SmallTest
+    public void testIsShortcutWithNumeric() throws Exception {
+        mMenu.setQwertyMode(false);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+        Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_2,
+                                              makeKeyEvent(KeyEvent.KEYCODE_2,  0)));
+        Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+                                               makeKeyEvent(KeyEvent.KEYCODE_A,  0)));
+    }
+
+    @SmallTest
+    public void testIsShortcutWithAlt() throws Exception {
+        mMenu.setQwertyMode(true);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+        Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+                                              makeKeyEvent(KeyEvent.KEYCODE_A,
+                                                           KeyEvent.META_ALT_ON)));
+        Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+                                              makeKeyEvent(KeyEvent.KEYCODE_A,
+                                                           KeyEvent.META_SYM_ON)));
+    }
+
+    @SmallTest
+    public void testIsNotShortcutWithShift() throws Exception {
+        mMenu.setQwertyMode(true);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+        Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+                                              makeKeyEvent(KeyEvent.KEYCODE_A,
+                                                           KeyEvent.META_SHIFT_ON)));
+    }
+
+    @SmallTest
+    public void testIsNotShortcutWithSym() throws Exception {
+        mMenu.setQwertyMode(true);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', 'a');
+        Assert.assertFalse(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+                                              makeKeyEvent(KeyEvent.KEYCODE_A,
+                                                           KeyEvent.META_SYM_ON)));
+    }
+    
+    @SmallTest
+    public void testIsShortcutWithUpperCaseAlpha() throws Exception {
+        mMenu.setQwertyMode(true);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', 'A');
+        Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_A,
+                                              makeKeyEvent(KeyEvent.KEYCODE_A,  0)));
+    }
+
+    @SmallTest
+    public void testIsShortcutWithBackspace() throws Exception {
+        mMenu.setQwertyMode(true);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', '\b');
+        Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_DEL,
+                                              makeKeyEvent(KeyEvent.KEYCODE_DEL,  0)));
+    }
+
+    @SmallTest
+    public void testIsShortcutWithNewline() throws Exception {
+        mMenu.setQwertyMode(true);
+        mMenu.add(0, 0, 0, "test").setShortcut('2', '\n');
+        Assert.assertTrue(mMenu.isShortcutKey(KeyEvent.KEYCODE_ENTER,
+                                              makeKeyEvent(KeyEvent.KEYCODE_ENTER,  0)));
+    }
+    
+    @SmallTest
+    public void testOrder() {
+        final String a = "a", b = "b", c = "c";
+        final int firstOrder = 7, midOrder = 8, lastOrder = 9;
+        
+        mMenu.add(0, 0, lastOrder, c);
+        mMenu.add(0, 0, firstOrder, a);
+        mMenu.add(0, 0, midOrder, b);
+        
+        Assert.assertEquals(firstOrder, mMenu.getItem(0).getOrder());
+        Assert.assertEquals(a, mMenu.getItem(0).getTitle());
+        Assert.assertEquals(midOrder, mMenu.getItem(1).getOrder());
+        Assert.assertEquals(b, mMenu.getItem(1).getTitle());
+        Assert.assertEquals(lastOrder, mMenu.getItem(2).getOrder());
+        Assert.assertEquals(c, mMenu.getItem(2).getTitle());
+    }
+
+    @SmallTest
+    public void testTitle() {
+        final String title = "test";
+        final MenuItem stringItem = mMenu.add(title);
+        final MenuItem resItem = mMenu.add(R.string.menu_test);
+        
+        Assert.assertEquals(title, stringItem.getTitle());
+        Assert.assertEquals(getContext().getResources().getString(R.string.menu_test), resItem
+                .getTitle());
+    }
+
+    @SmallTest
+    public void testCheckable() {
+        final int groupId = 1;
+        final MenuItem item1 = mMenu.add(groupId, 1, 0, "item1");
+        final MenuItem item2 = mMenu.add(groupId, 2, 0, "item2");
+        
+        // Set to exclusive
+        mMenu.setGroupCheckable(groupId, true, true);
+        Assert.assertTrue("Item was not set to checkable", item1.isCheckable());
+        item1.setChecked(true);
+        Assert.assertTrue("Item did not get checked", item1.isChecked());
+        Assert.assertFalse("Item was not unchecked due to exclusive checkable", item2.isChecked());
+        mMenu.findItem(2).setChecked(true);
+        Assert.assertTrue("Item did not get checked", item2.isChecked());
+        Assert.assertFalse("Item was not unchecked due to exclusive checkable", item1.isChecked());
+        
+        // Multiple non-exlusive checkable items
+        mMenu.setGroupCheckable(groupId, true, false);
+        Assert.assertTrue("Item was not set to checkable", item1.isCheckable());
+        item1.setChecked(false);
+        Assert.assertFalse("Item did not get unchecked", item1.isChecked());
+        item1.setChecked(true);
+        Assert.assertTrue("Item did not get checked", item1.isChecked());
+        mMenu.findItem(2).setChecked(true);
+        Assert.assertTrue("Item did not get checked", item2.isChecked());
+        Assert.assertTrue("Item was unchecked when it shouldnt have been", item1.isChecked());
+    }
+    
+    @SmallTest
+    public void testVisibility() {
+        final MenuItem item1 = mMenu.add(0, 1, 0, "item1");
+        final MenuItem item2 = mMenu.add(0, 2, 0, "item2");
+        
+        // Should start as visible
+        Assert.assertTrue("Item did not start as visible", item1.isVisible());
+        Assert.assertTrue("Item did not start as visible", item2.isVisible());
+
+        // Hide
+        item1.setVisible(false);
+        Assert.assertFalse("Item did not become invisible", item1.isVisible());
+        mMenu.findItem(2).setVisible(false);
+        Assert.assertFalse("Item did not become invisible", item2.isVisible());
+    }
+    
+    @SmallTest
+    public void testSubMenu() {
+        final SubMenu subMenu = mMenu.addSubMenu(0, 0, 0, "submenu");
+        final MenuItem subMenuItem = subMenu.getItem();
+        final MenuItem item1 = subMenu.add(0, 1, 0, "item1");
+        final MenuItem item2 = subMenu.add(0, 2, 0, "item2");
+        
+        // findItem should recurse into submenus
+        Assert.assertEquals(item1, mMenu.findItem(1));
+        Assert.assertEquals(item2, mMenu.findItem(2));
+    }
+    
+    @SmallTest
+    public void testRemove() {
+        final int groupId = 1;
+        final MenuItem item1 = mMenu.add(groupId, 1, 0, "item1");
+        final MenuItem item2 = mMenu.add(groupId, 2, 0, "item2");
+        final MenuItem item3 = mMenu.add(groupId, 3, 0, "item3");
+        final MenuItem item4 = mMenu.add(groupId, 4, 0, "item4");
+        final MenuItem item5 = mMenu.add(groupId, 5, 0, "item5");
+        final MenuItem item6 = mMenu.add(0, 6, 0, "item6");
+        
+        Assert.assertEquals(item1, mMenu.findItem(1));
+        mMenu.removeItemAt(0);
+        Assert.assertNull(mMenu.findItem(1));
+        
+        Assert.assertEquals(item2, mMenu.findItem(2));
+        mMenu.removeItem(2);
+        Assert.assertNull(mMenu.findItem(2));
+        
+        Assert.assertEquals(item3, mMenu.findItem(3));
+        Assert.assertEquals(item4, mMenu.findItem(4));
+        Assert.assertEquals(item5, mMenu.findItem(5));
+        mMenu.removeGroup(groupId);
+        Assert.assertNull(mMenu.findItem(3));
+        Assert.assertNull(mMenu.findItem(4));
+        Assert.assertNull(mMenu.findItem(5));
+        
+        Assert.assertEquals(item6, mMenu.findItem(6));
+        mMenu.clear();
+        Assert.assertNull(mMenu.findItem(6));
+    }
+    
+    private KeyEvent makeKeyEvent(int keyCode, int metaState) {
+        return new KeyEvent(0L, 0L, KeyEvent.ACTION_DOWN, keyCode, 0, metaState);
+    }
+}
diff --git a/core/tests/coretests/src/android/webkit/WebkitTest.java b/core/tests/coretests/src/android/webkit/WebkitTest.java
new file mode 100644
index 0000000..17b4088
--- /dev/null
+++ b/core/tests/coretests/src/android/webkit/WebkitTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.test.AndroidTestCase;
+import android.text.format.DateFormat;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+import android.webkit.DateSorter;
+
+import java.util.Calendar;
+import java.util.Date;
+
+public class WebkitTest extends AndroidTestCase {
+
+    private static final String LOGTAG = WebkitTest.class.getName();
+
+    @MediumTest
+    public void testDateSorter() throws Exception {
+        /**
+         * Note: check the logging output manually to test
+         * nothing automated yet, besides object creation
+         */
+        DateSorter dateSorter = new DateSorter(mContext);
+        Date date = new Date();
+
+        for (int i = 0; i < DateSorter.DAY_COUNT; i++) {
+            Log.i(LOGTAG, "Boundary " + i + " " + dateSorter.getBoundary(i));
+            Log.i(LOGTAG, "Label " + i + " " + dateSorter.getLabel(i));
+        }
+
+        Calendar c = Calendar.getInstance();
+        long time = c.getTimeInMillis();
+        int index;
+        Log.i(LOGTAG, "now: " + dateSorter.getIndex(time));
+        for (int i = 0; i < 20; i++) {
+            time -= 8 * 60 * 60 * 1000; // 8 hours
+            date.setTime(time);
+            c.setTime(date);
+            index = dateSorter.getIndex(time);
+            Log.i(LOGTAG, "time: " + DateFormat.format("yyyy/MM/dd kk:mm:ss", c).toString() +
+                    " " + index + " " + dateSorter.getLabel(index));
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/LabelView.java b/core/tests/coretests/src/android/widget/LabelView.java
new file mode 100644
index 0000000..4661c01
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/LabelView.java
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+package android.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+/**
+ * Example of how to write a custom subclass of View. LabelView
+ * is used to draw simple text views. Note that it does not handle
+ * styled text or right-to-left writing systems.
+ *
+ */
+public class LabelView extends View {
+    /**
+     * Constructor.  This version is only needed if you will be instantiating
+     * the object manually (not from a layout XML file).
+     * @param context the application environment
+     */
+    public LabelView(Context context) {
+        super(context);
+        initLabelView();
+    }
+
+    /**
+     * Construct object, initializing with any attributes we understand from a
+     * layout file. These attributes are defined in
+     * SDK/assets/res/any/classes.xml.
+     * 
+     * @see android.view.View#View(android.content.Context, android.util.AttributeSet)
+    public LabelView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initLabelView();
+
+        Resources.StyledAttributes a = context.obtainStyledAttributes(attrs,
+                R.styleable.LabelView);
+
+        CharSequence s = a.getString(R.styleable.LabelView_text);
+        if (s != null) {
+            setText(s.toString());
+        }
+
+        ColorStateList textColor = a.getColorList(R.styleable.
+                                                  LabelView_textColor);
+        if (textColor != null) {
+            setTextColor(textColor.getDefaultColor(0));
+        }
+
+        int textSize = a.getInt(R.styleable.LabelView_textSize, 0);
+        if (textSize > 0) {
+            setTextSize(textSize);
+        }
+
+        a.recycle();
+    }
+
+     */
+    private void initLabelView() {
+        mTextPaint = new Paint();
+        mTextPaint.setAntiAlias(true);
+        mTextPaint.setTextSize(16);
+        mTextPaint.setColor(0xFF000000);
+
+        mPaddingLeft = 3;
+        mPaddingTop = 3;
+        mPaddingRight = 3;
+        mPaddingBottom = 3;
+    }
+
+    /**
+     * Sets the text to display in this label
+     * @param text The text to display. This will be drawn as one line.
+     */
+    public void setText(String text) {
+        mText = text;
+        requestLayout();
+        invalidate();
+    }
+
+    /**
+     * Sets the text size for this label
+     * @param size Font size
+     */
+    public void setTextSize(int size) {
+        mTextPaint.setTextSize(size);
+        requestLayout();
+        invalidate();
+    }
+
+    /**
+     * Sets the text color for this label
+     * @param color ARGB value for the text
+     */
+    public void setTextColor(int color) {
+        mTextPaint.setColor(color);
+        invalidate();
+    }
+
+
+    /**
+     * @see android.view.View#measure(int, int)
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(measureWidth(widthMeasureSpec),
+                measureHeight(heightMeasureSpec));
+    }
+
+    /**
+     * Determines the width of this view
+     * @param measureSpec A measureSpec packed into an int
+     * @return The width of the view, honoring constraints from measureSpec
+     */
+    private int measureWidth(int measureSpec) {
+        int result;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        if (specMode == MeasureSpec.EXACTLY) {
+            // We were told how big to be
+            result = specSize;
+        } else {
+            // Measure the text
+            result = (int) mTextPaint.measureText(mText) + mPaddingLeft
+                    + mPaddingRight;
+            if (specMode == MeasureSpec.AT_MOST) {
+                // Respect AT_MOST value if that was what is called for by measureSpec
+                result = Math.min(result, specSize);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Determines the height of this view
+     * @param measureSpec A measureSpec packed into an int
+     * @return The height of the view, honoring constraints from measureSpec
+     */
+    private int measureHeight(int measureSpec) {
+        int result;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        mAscent = (int) mTextPaint.ascent();
+        if (specMode == MeasureSpec.EXACTLY) {
+            // We were told how big to be
+            result = specSize;
+        } else {
+            // Measure the text (beware: ascent is a negative number)
+            result = (int) (-mAscent + mTextPaint.descent()) + mPaddingTop
+                    + mPaddingBottom;
+            if (specMode == MeasureSpec.AT_MOST) {
+                // Respect AT_MOST value if that was what is called for by measureSpec
+                result = Math.min(result, specSize);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Render the text
+     * 
+     * @see android.view.View#onDraw(android.graphics.Canvas)
+     */
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        canvas.drawText(mText, mPaddingLeft, mPaddingTop - mAscent, mTextPaint);
+    }
+
+    private Paint mTextPaint;
+    private String mText;
+    private int mAscent;
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
new file mode 100644
index 0000000..c25df7c
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewPerformanceTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.widget;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.SpannedString;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class TextViewPerformanceTest extends AndroidTestCase {
+
+    private String mString = "The quick brown fox";
+    private Canvas mCanvas;
+    private PerformanceTextView mTextView;
+    private Paint mPaint;
+    private PerformanceLabelView mLabelView;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Bitmap mBitmap = Bitmap.createBitmap(320, 240, Bitmap.Config.RGB_565);
+        mCanvas = new Canvas(mBitmap);
+
+        ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(320, 240);
+
+        mLabelView = new PerformanceLabelView(mContext);
+        mLabelView.setText(mString);
+        mLabelView.measure(View.MeasureSpec.AT_MOST | 320, View.MeasureSpec.AT_MOST | 240);
+        mLabelView.mySetFrame(320, 240);
+        mLabelView.setLayoutParams(p);
+        mLabelView.myDraw(mCanvas);
+
+        mPaint = new Paint();
+        mCanvas.save();
+        mTextView = new PerformanceTextView(mContext);
+        mTextView.setLayoutParams(p);
+        mTextView.setText(mString);
+        mTextView.mySetFrame(320, 240);
+        mTextView.measure(View.MeasureSpec.AT_MOST | 320, View.MeasureSpec.AT_MOST | 240);
+    }
+
+    @MediumTest
+    public void testDrawTextViewLine() throws Exception {
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+    }
+
+    @SmallTest
+    public void testSpan() throws Exception {
+        CharSequence charSeq = new SpannedString(mString);
+        mTextView.setText(charSeq);
+
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+        mTextView.myDraw(mCanvas);
+    }
+
+    @SmallTest
+    public void testCanvasDrawText() throws Exception {
+        mCanvas.drawText(mString, 30, 30, mPaint);
+    }
+
+    @SmallTest
+    public void testLabelViewDraw() throws Exception {
+        mLabelView.myDraw(mCanvas);
+    }
+
+    private class PerformanceTextView extends TextView {
+        public PerformanceTextView(Context context) {
+            super(context);
+        }
+
+        final void myDraw(Canvas c) {
+            super.onDraw(c);
+        }
+
+        final void mySetFrame(int w, int h) {
+            super.setFrame(0, 0, w, h);
+        }
+    }
+
+    private class PerformanceLabelView extends LabelView {
+        public PerformanceLabelView(Context context) {
+            super(context);
+        }
+
+        final void myDraw(Canvas c) {
+            super.onDraw(c);
+        }
+
+        final void mySetFrame(int w, int h) {
+            super.setFrame(0, 0, w, h);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
new file mode 100644
index 0000000..d8d145c
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.widget;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.GetChars;
+import android.widget.TextView;
+
+/**
+ * TextViewTest tests {@link TextView}.
+ */
+public class TextViewTest extends AndroidTestCase {
+
+    @SmallTest
+    public void testArray() throws Exception {
+        TextView tv = new TextView(mContext);
+
+        char[] c = new char[] { 'H', 'e', 'l', 'l', 'o', ' ',
+                                'W', 'o', 'r', 'l', 'd', '!' };
+
+        tv.setText(c, 1, 4);
+        CharSequence oldText = tv.getText();
+
+        tv.setText(c, 4, 5);
+        CharSequence newText = tv.getText();
+
+        assertTrue(newText == oldText);
+
+        assertEquals(5, newText.length());
+        assertEquals('o', newText.charAt(0));
+        assertEquals("o Wor", newText.toString());
+
+        assertEquals(" Wo", newText.subSequence(1, 4));
+
+        char[] c2 = new char[7];
+        ((GetChars) newText).getChars(1, 4, c2, 2);
+        assertEquals('\0', c2[1]);
+        assertEquals(' ', c2[2]);
+        assertEquals('W', c2[3]);
+        assertEquals('o', c2[4]);
+        assertEquals('\0', c2[5]);
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java b/core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java
new file mode 100644
index 0000000..a304b68
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/BitwiseStreamsTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.internal.util;
+
+import com.android.internal.util.BitwiseInputStream;
+import com.android.internal.util.BitwiseOutputStream;
+import com.android.internal.util.HexDump;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import android.util.Log;
+
+import java.util.Random;
+
+public class BitwiseStreamsTest extends AndroidTestCase {
+    private final static String LOG_TAG = "BitwiseStreamsTest";
+
+    @SmallTest
+    public void testOne() throws Exception {
+        int offset = 3;
+        byte[] inBuf = HexDump.hexStringToByteArray("FFDD");
+        BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+        outStream.skip(offset);
+        for (int i = 0; i < inBuf.length; i++) outStream.write(8, inBuf[i]);
+        byte[] outBuf = outStream.toByteArray();
+        BitwiseInputStream inStream = new BitwiseInputStream(outBuf);
+        byte[] inBufDup = new byte[inBuf.length];
+        inStream.skip(offset);
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+        assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+    }
+
+    @SmallTest
+    public void testTwo() throws Exception {
+        int offset = 3;
+        byte[] inBuf = HexDump.hexStringToByteArray("11d4f29c0e9ad3c36e72584e064d9b53");
+        BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+        outStream.skip(offset);
+        for (int i = 0; i < inBuf.length; i++) outStream.write(8, inBuf[i]);
+        BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+        inStream.skip(offset);
+        byte[] inBufDup = new byte[inBuf.length];
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+        assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+    }
+
+    @SmallTest
+    public void testThree() throws Exception {
+        int offset = 4;
+        byte[] inBuf = HexDump.hexStringToByteArray("00031040900112488ea794e0");
+        BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+        outStream.skip(offset);
+        for (int i = 0; i < inBuf.length; i++) outStream.write(8, inBuf[i]);
+        BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+        inStream.skip(offset);
+        byte[] inBufDup = new byte[inBuf.length];
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+        assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+    }
+
+    @SmallTest
+    public void testFour() throws Exception {
+        int offset = 7;
+        byte[] inBuf = HexDump.hexStringToByteArray("00031040900112488ea794e0");
+        BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+        outStream.skip(offset);
+        for (int i = 0; i < inBuf.length; i++) {
+            outStream.write(5, inBuf[i] >>> 3);
+            outStream.write(3, inBuf[i] & 0x07);
+        }
+        BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+        inStream.skip(offset);
+        byte[] inBufDup = new byte[inBuf.length];
+        for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+        assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+    }
+
+    @SmallTest
+    public void testFive() throws Exception {
+        Random random = new Random();
+        int iterations = 10000;
+        int[] sizeArr = new int[iterations];
+        int[] valueArr = new int[iterations];
+        BitwiseOutputStream outStream = new BitwiseOutputStream(iterations * 4);
+        for (int i = 0; i < iterations; i++) {
+            int x = random.nextInt();
+            int size = (x & 0x07) + 1;
+            int value = x & (-1 >>> (32 - size));
+            sizeArr[i] = size;
+            valueArr[i] = value;
+            outStream.write(size, value);
+        }
+        BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+        for (int i = 0; i < iterations; i++) {
+            assertEquals(valueArr[i], inStream.read(sizeArr[i]));
+        }
+    }
+
+    @SmallTest
+    public void testSix() throws Exception {
+        int num_runs = 10;
+        long start = android.os.SystemClock.elapsedRealtime();
+        for (int run = 0; run < num_runs; run++) {
+            int offset = run % 8;
+            byte[] inBuf = HexDump.hexStringToByteArray("00031040900112488ea794e0");
+            BitwiseOutputStream outStream = new BitwiseOutputStream(30);
+            outStream.skip(offset);
+            for (int i = 0; i < inBuf.length; i++) {
+                outStream.write(5, inBuf[i] >>> 3);
+                outStream.write(3, inBuf[i] & 0x07);
+            }
+            BitwiseInputStream inStream = new BitwiseInputStream(outStream.toByteArray());
+            inStream.skip(offset);
+            byte[] inBufDup = new byte[inBuf.length];
+            for (int i = 0; i < inBufDup.length; i++) inBufDup[i] = (byte)inStream.read(8);
+            assertEquals(HexDump.toHexString(inBuf), HexDump.toHexString(inBufDup));
+        }
+        long end = android.os.SystemClock.elapsedRealtime();
+        Log.d(LOG_TAG, "repeated encode-decode took " + (end - start) + " ms");
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java b/core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java
new file mode 100644
index 0000000..55d186c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/CharSequencesTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.util;
+
+import com.android.internal.util.CharSequences;
+import static com.android.internal.util.CharSequences.forAsciiBytes;
+import junit.framework.TestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class CharSequencesTest extends TestCase {
+
+    @SmallTest
+    public void testCharSequences() {
+        String s = "Crazy Bob";
+        byte[] bytes = s.getBytes();
+
+        String copy = toString(forAsciiBytes(bytes));
+        assertTrue(s.equals(copy));
+
+        copy = toString(forAsciiBytes(bytes, 0, s.length()));
+        assertTrue(s.equals(copy));
+
+        String crazy = toString(forAsciiBytes(bytes, 0, 5));
+        assertTrue("Crazy".equals(crazy));
+
+        String a = toString(forAsciiBytes(bytes, 0, 3).subSequence(2, 3));
+        assertTrue("a".equals(a));
+
+        String empty = toString(forAsciiBytes(bytes, 0, 3).subSequence(3, 3));
+        assertTrue("".equals(empty));
+
+        assertTrue(CharSequences.equals("bob", "bob"));
+        assertFalse(CharSequences.equals("b", "bob"));
+        assertFalse(CharSequences.equals("", "bob"));
+    }
+
+    /**
+     * Converts a CharSequence to a string the slow way. Useful for testing
+     * a CharSequence implementation.
+     */
+    static String toString(CharSequence charSequence) {
+        return new StringBuilder().append(charSequence).toString();
+    }
+}
diff --git a/core/tests/coretests/src/com/google/android/net/ParentalControlTest.java b/core/tests/coretests/src/com/google/android/net/ParentalControlTest.java
new file mode 100644
index 0000000..d8ffeab
--- /dev/null
+++ b/core/tests/coretests/src/com/google/android/net/ParentalControlTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.google.android.net;
+
+import com.google.android.net.ParentalControl;
+import com.google.android.net.ParentalControlState;
+
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+public class ParentalControlTest extends AndroidTestCase {
+
+    private boolean mOnResultCalled = false;
+
+    public class Callback implements ParentalControl.Callback {
+        public void onResult(ParentalControlState state) {
+            synchronized (ParentalControlTest.class) {
+                mOnResultCalled = true;
+                ParentalControlTest.class.notifyAll();
+            }
+        }
+    }
+
+    @SmallTest
+    public void testParentalControlCallback() {
+        synchronized (ParentalControlTest.class) {
+            ParentalControl.getParentalControlState(new Callback(), null);
+            try {
+                long start = SystemClock.uptimeMillis();
+                ParentalControlTest.class.wait(20 * 1000);
+                long end = SystemClock.uptimeMillis();
+                Log.d("AndroidTests", "ParentalControlTest callback took " + (end-start) + " ms.");
+            } catch (InterruptedException ex) {
+            }
+        }
+
+        Assert.assertTrue(mOnResultCalled);
+    }
+}